tor_proto/tunnel/circuit/
celltypes.rs

1//! Wrapper types for subsets of ChanMsg and RelayMsg types.
2//!
3//! These wrappers define types that are valid in response to particular
4//! request, or when received in particular circumstances.  They're used
5//! so that Rust's typesafety can help enforce protocol properties.
6
7use crate::{Error, Result};
8use derive_deftly::Deftly;
9use std::fmt::{self, Display};
10use tor_cell::chancell::{
11    msg::{self as chanmsg, AnyChanMsg},
12    ChanMsg,
13};
14use tor_memquota::derive_deftly_template_HasMemoryCost;
15
16/// A subclass of ChanMsg that can arrive in response to a CREATE* cell
17/// that we send.
18#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
19#[derive(Debug)]
20#[allow(unreachable_pub)] // Only `pub` with feature `testing`; otherwise, visible in crate
21#[allow(clippy::exhaustive_enums)]
22pub enum CreateResponse {
23    /// Destroy cell: the CREATE failed.
24    Destroy(chanmsg::Destroy),
25    /// CreatedFast: good response to a CREATE cell.
26    CreatedFast(chanmsg::CreatedFast),
27    /// Created2: good response to a CREATE2 cell.
28    Created2(chanmsg::Created2),
29}
30
31impl Display for CreateResponse {
32    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33        use CreateResponse as CR;
34        match self {
35            CR::Destroy(destroy) => write!(f, "DESTROY({})", destroy.reason()),
36            CR::CreatedFast(_) => Display::fmt("CREATED_FAST", f),
37            CR::Created2(_) => Display::fmt("CREATED2", f),
38        }
39    }
40}
41
42impl TryFrom<AnyChanMsg> for CreateResponse {
43    type Error = crate::Error;
44
45    fn try_from(m: AnyChanMsg) -> Result<CreateResponse> {
46        match m {
47            AnyChanMsg::Destroy(m) => Ok(CreateResponse::Destroy(m)),
48            AnyChanMsg::CreatedFast(m) => Ok(CreateResponse::CreatedFast(m)),
49            AnyChanMsg::Created2(m) => Ok(CreateResponse::Created2(m)),
50            _ => Err(Error::ChanProto(format!(
51                "Got a {} in response to circuit creation",
52                m.cmd()
53            ))),
54        }
55    }
56}
57
58/// A subclass of ChanMsg that can correctly arrive on a live client
59/// circuit (one where a CREATED* has been received).
60#[derive(Debug, Deftly)]
61#[allow(unreachable_pub)] // Only `pub` with feature `testing`; otherwise, visible in crate
62#[derive_deftly(HasMemoryCost)]
63pub enum ClientCircChanMsg {
64    /// A relay cell telling us some kind of remote command from some
65    /// party on the circuit.
66    Relay(chanmsg::Relay),
67    /// A cell telling us to destroy the circuit.
68    Destroy(chanmsg::Destroy),
69    // Note: RelayEarly is not valid for clients!
70}
71
72impl TryFrom<AnyChanMsg> for ClientCircChanMsg {
73    type Error = crate::Error;
74
75    fn try_from(m: AnyChanMsg) -> Result<ClientCircChanMsg> {
76        match m {
77            AnyChanMsg::Destroy(m) => Ok(ClientCircChanMsg::Destroy(m)),
78            AnyChanMsg::Relay(m) => Ok(ClientCircChanMsg::Relay(m)),
79            _ => Err(Error::ChanProto(format!(
80                "Got a {} cell on an open circuit",
81                m.cmd()
82            ))),
83        }
84    }
85}
86
87#[cfg(test)]
88mod test {
89    // @@ begin test lint list maintained by maint/add_warning @@
90    #![allow(clippy::bool_assert_comparison)]
91    #![allow(clippy::clone_on_copy)]
92    #![allow(clippy::dbg_macro)]
93    #![allow(clippy::mixed_attributes_style)]
94    #![allow(clippy::print_stderr)]
95    #![allow(clippy::print_stdout)]
96    #![allow(clippy::single_char_pattern)]
97    #![allow(clippy::unwrap_used)]
98    #![allow(clippy::unchecked_duration_subtraction)]
99    #![allow(clippy::useless_vec)]
100    #![allow(clippy::needless_pass_by_value)]
101    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
102    use super::*;
103
104    #[test]
105    fn create_response() {
106        use tor_cell::chancell::msg::{self, AnyChanMsg};
107        fn good(m: AnyChanMsg) {
108            assert!(CreateResponse::try_from(m).is_ok());
109        }
110        fn bad(m: AnyChanMsg) {
111            assert!(CreateResponse::try_from(m).is_err());
112        }
113
114        good(msg::Destroy::new(2.into()).into());
115        good(msg::CreatedFast::new(&b"this offer is unrepeatable"[..]).into());
116        good(msg::Created2::new(&b"guaranteed guaranteed"[..]).into());
117        bad(msg::CreateFast::new(&b"for a lifetime or more"[..]).into());
118        bad(msg::Versions::new([1, 2, 3]).unwrap().into());
119    }
120
121    #[test]
122    fn client_circ_chan_msg() {
123        use tor_cell::chancell::msg::{self, AnyChanMsg};
124        fn good(m: AnyChanMsg) {
125            assert!(ClientCircChanMsg::try_from(m).is_ok());
126        }
127        fn bad(m: AnyChanMsg) {
128            assert!(ClientCircChanMsg::try_from(m).is_err());
129        }
130
131        good(msg::Destroy::new(2.into()).into());
132        bad(msg::CreatedFast::new(&b"guaranteed in this world"[..]).into());
133        bad(msg::Created2::new(&b"and the next"[..]).into());
134        good(msg::Relay::new(&b"guaranteed guaranteed"[..]).into());
135        bad(msg::AnyChanMsg::RelayEarly(
136            msg::Relay::new(&b"for the world and its mother"[..]).into(),
137        ));
138        bad(msg::Versions::new([1, 2, 3]).unwrap().into());
139    }
140}