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

            
7
use derive_deftly::{Deftly, define_derive_deftly};
8
use std::fmt::{self, Display};
9
use tor_cell::chancell::BoxedCellBody;
10
use tor_cell::chancell::msg::{self as chanmsg};
11
use tor_cell::restricted_msg;
12

            
13
define_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
630
        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

            
47
pub(crate) use derive_deftly_template_RestrictedChanMsgSet;
48

            
49
restricted_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")]
67
pub 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

            
76
impl 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

            
87
restricted_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

            
96
impl RelayMaybeEarlyChanMsg {
97
    /// Consume this message and return a `BoxedCellBody` for encryption/decryption.
98
52
    pub(crate) fn into_relay_body(self) -> BoxedCellBody {
99
52
        match self {
100
32
            Self::Relay(x) => x.into_relay_body(),
101
20
            Self::RelayEarly(x) => x.into_relay_body(),
102
        }
103
52
    }
104
}
105

            
106
#[cfg(test)]
107
mod 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
}