Skip to main content

tor_proto/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 derive_deftly::{Deftly, define_derive_deftly};
8use std::fmt::{self, Display};
9use tor_cell::chancell::BoxedCellBody;
10use tor_cell::chancell::msg::{self as chanmsg};
11use tor_cell::restricted_msg;
12
13define_derive_deftly! {
14    /// Derives a `TryFrom<AnyChanMsg>` implementation for enums
15    /// that represent restricted subsets of ChanMsgs
16    ///
17    /// # Limitations
18    ///
19    /// The variants of the enum this is derived for *must* be a
20    /// subset of the variants of [`AnyChanMsg`].
21    RestrictedChanMsgSet:
22
23    impl TryFrom<tor_cell::chancell::msg::AnyChanMsg> for $ttype {
24        type Error = $crate::Error;
25
26        fn try_from(m: tor_cell::chancell::msg::AnyChanMsg) -> $crate::Result<$ttype> {
27            match m {
28                $( tor_cell::chancell::msg::AnyChanMsg::$vname(m) => Ok($ttype::$vname(m)), )
29                _ => Err($crate::Error::ChanProto(format!(
30                    "Got a {} {}",
31                    <tor_cell::chancell::msg::AnyChanMsg as tor_cell::chancell::ChanMsg>::cmd(&m),
32                    ${tmeta(usage) as str},
33                ))),
34            }
35        }
36    }
37
38    impl From<$ttype> for tor_cell::chancell::msg::AnyChanMsg {
39        fn from(m: $ttype) -> tor_cell::chancell::msg::AnyChanMsg {
40            match m {
41                $( $ttype::$vname(m) => m.into(), )
42            }
43        }
44    }
45}
46
47pub(crate) use derive_deftly_template_RestrictedChanMsgSet;
48
49restricted_msg! {
50    /// A subset of [`ChanMsg`] that can be used to create a circuit.
51    #[derive(Debug)]
52    #[allow(clippy::exhaustive_enums)]
53    pub(crate) enum CreateRequest : ChanMsg {
54        CreateFast,
55        Create2,
56    }
57}
58
59/// A subclass of ChanMsg that can arrive in response to a CREATE* cell
60/// that we send.
61#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
62#[derive(Debug, Deftly)]
63#[allow(unreachable_pub)] // Only `pub` with feature `testing`; otherwise, visible in crate
64#[allow(clippy::exhaustive_enums)]
65#[derive_deftly(RestrictedChanMsgSet)]
66#[deftly(usage = "in response to circuit creation")]
67pub enum CreateResponse {
68    /// Destroy cell: the CREATE failed.
69    Destroy(chanmsg::Destroy),
70    /// CreatedFast: good response to a CREATE cell.
71    CreatedFast(chanmsg::CreatedFast),
72    /// Created2: good response to a CREATE2 cell.
73    Created2(chanmsg::Created2),
74}
75
76impl Display for CreateResponse {
77    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78        use CreateResponse as CR;
79        match self {
80            CR::Destroy(destroy) => write!(f, "DESTROY({})", destroy.reason()),
81            CR::CreatedFast(_) => Display::fmt("CREATED_FAST", f),
82            CR::Created2(_) => Display::fmt("CREATED2", f),
83        }
84    }
85}
86
87restricted_msg! {
88    /// A RELAY or RELAY_EARLY channel message.
89    #[derive(Debug)]
90    pub(crate) enum RelayMaybeEarlyChanMsg : ChanMsg {
91        Relay,
92        RelayEarly,
93    }
94}
95
96impl RelayMaybeEarlyChanMsg {
97    /// Consume this message and return a `BoxedCellBody` for encryption/decryption.
98    pub(crate) fn into_relay_body(self) -> BoxedCellBody {
99        match self {
100            Self::Relay(x) => x.into_relay_body(),
101            Self::RelayEarly(x) => x.into_relay_body(),
102        }
103    }
104}
105
106#[cfg(test)]
107mod test {
108    // @@ begin test lint list maintained by maint/add_warning @@
109    #![allow(clippy::bool_assert_comparison)]
110    #![allow(clippy::clone_on_copy)]
111    #![allow(clippy::dbg_macro)]
112    #![allow(clippy::mixed_attributes_style)]
113    #![allow(clippy::print_stderr)]
114    #![allow(clippy::print_stdout)]
115    #![allow(clippy::single_char_pattern)]
116    #![allow(clippy::unwrap_used)]
117    #![allow(clippy::unchecked_time_subtraction)]
118    #![allow(clippy::useless_vec)]
119    #![allow(clippy::needless_pass_by_value)]
120    #![allow(clippy::string_slice)] // See arti#2571
121    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
122
123    use super::*;
124
125    #[test]
126    fn create_response() {
127        use tor_cell::chancell::msg::{self, AnyChanMsg};
128        fn good(m: AnyChanMsg) {
129            assert!(CreateResponse::try_from(m).is_ok());
130        }
131        fn bad(m: AnyChanMsg) {
132            assert!(CreateResponse::try_from(m).is_err());
133        }
134
135        good(msg::Destroy::new(2.into()).into());
136        good(msg::CreatedFast::new(&b"this offer is unrepeatable"[..]).into());
137        good(msg::Created2::new(&b"guaranteed guaranteed"[..]).into());
138        bad(msg::CreateFast::new(&b"for a lifetime or more"[..]).into());
139        bad(msg::Versions::new([1, 2, 3]).unwrap().into());
140    }
141}