1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//! Parameters influencing all channels in a Tor client
//!
//! This module contains [`ChannelPaddingInstructions`].
//!
//! These are instructions about what to do about padding.
//! They include information about:
//!   * whether padding is to be sent
//!   * what timing parameters to use for sending padding
//!   * what `PADDING_NEGOTIATE` cell to send
//!
//! The instructions are, ultimately, instructions to the channel reactor.
//! The reactor gets a [`ChannelPaddingInstructionsUpdates`],
//! which is a set of *changes* to make.
//!
//! The producer side is the channel manager,
//! which records the current `ChannelPaddingInstructions`
//! and makes updates with [`ChannelPaddingInstructionsUpdatesBuilder`]
//! (and is assisted by [`Channel::engage_padding_activities`]).
//!
//! [`Channel::engage_padding_activities`]: super::Channel::engage_padding_activities

use educe::Educe;

use tor_cell::chancell::msg::PaddingNegotiate;

use super::padding;

/// Generate most of the types and methods relating to ChannelPaddingInstructions:
/// things which contain or process all instructions fields (or each one)
///
/// There is one call to this macro, which has as argument
/// the body of `struct ChannelPaddingInstructions`, with the following differences:
///
///  * field visibility specifiers are not specified; they are provided by the macro
///  * non-doc attributes that ought to be applied to fields in `ChannelPaddingInstructions`
///    are prefixed with `field`, e.g. `#[field educe(Default ...)]`;
///    this allows applying doc attributes to other items too.
///
/// Generates, fairly straightforwardly:
///
/// ```ignore
/// pub struct ChannelPaddingInstructions { ... } // containing the fields as specified
/// pub struct ChannelPaddingInstructionsUpdates { ... } // containing `Option` of each field
/// pub fn ChannelPaddingInstructions::initial_update(&self) -> ChannelPaddingInstructionsUpdates;
/// pub fn ChannelPaddingInstructionsUpdatesBuilder::$field(self, new_value: _) -> Self;
/// ```
///
/// Within the macro body, we indent the per-field `$( )*` with 2 spaces.
macro_rules! define_channels_insns_and_automatic_impls { { $(
    $( #[doc $($doc_attr:tt)*] )*
    $( #[field $other_attr:meta] )*
    $field:ident : $ty:ty
),* $(,)? } => {

    /// Initial, and, overall, padding instructions for channels
    ///
    /// This is used both to generate the initial instructions,
    /// and to handle updates:
    /// when used for handling updates,
    /// it contains the last instructions that has been implemented.
    ///
    /// Central code managing all channels will contain a `ChannelPaddingInstructions`,
    /// and use `ChannelPaddingInstructionsUpdatesBuilder` to both update those `Instructions`
    /// and generate `ChannelPaddingInstructionsUpdates` messages representing the changes.
    ///
    /// The channel frontend (methods on `Channel`)
    /// processes `ChannelPaddingInstructionsUpdates` from the channel manager,
    /// possibly into channel-specific updates.
    ///
    /// `Default` is a placeholder to use pending availability of a netdir etc.
    #[derive(Debug, Educe, Clone, Eq, PartialEq)]
    #[educe(Default)]
    pub struct ChannelPaddingInstructions {
      $(
        $( #[doc $($doc_attr)*] )*
        $( #[$other_attr] )*
        pub(crate) $field: $ty,
      )*
    }

    /// New instructions to the reactor
    ///
    /// Can contain updates to each of the fields in `ChannelPaddingInstructions`.
    /// Constructed via [`ChannelPaddingInstructionsUpdatesBuilder`],
    /// which is obtained from [`ChannelPaddingInstructions::start_update`].
    ///
    /// Sent to all channel implementations, when they ought to change their behaviour.
    #[derive(Debug, Default, Clone, Eq, PartialEq)]
    pub struct ChannelPaddingInstructionsUpdates {
      $(
        /// New value, if it has changed.
        ///
        /// Having this contain `Option` allows the sender of an update to promise
        /// that the value hasn't changed, and thereby allows the channel implementation
        /// to avoid touching state that it doesn't need to (eg, timers).
        pub(crate) $field: Option<$ty>,
      )*
    }

    impl ChannelPaddingInstructions {
        /// Create an update message which sets settings in `self` which
        /// are not equal to the initial behaviour of the reactor.
        ///
        /// Used during channel startup.
        #[must_use = "initial_update makes an updates message that must be sent to have effect"]
        pub fn initial_update(&self) -> Option<ChannelPaddingInstructionsUpdates> {
            let mut supposed = ChannelPaddingInstructions::default();

            supposed.start_update()
              $(
                .$field(self.$field.clone())
              )*
                .finish()
        }
    }

    impl<'c> ChannelPaddingInstructionsUpdatesBuilder<'c> {
      $(
        $( #[doc $($doc_attr)*] )*
        ///
        /// (Adds this setting to the update, if it has changed.)
        pub fn $field(mut self, new_value: $ty) -> Self {
            if &new_value != &self.insns.$field {
                self
                    .update
                    .get_or_insert_with(|| Default::default())
                    .$field = Some(new_value.clone());
                self.insns.$field = new_value;
            }
            self
        }
      )*
    }

    impl ChannelPaddingInstructionsUpdates {
        /// Combines `more` into `self`
        ///
        /// Values from `more` override ones in `self`.
        pub fn combine(&mut self, more: &Self) {
          $(
            if let Some(new_value) = &more.$field {
                self.$field = Some(new_value.clone());
            }
          )*
        }

      $(
        #[cfg(feature = "testing")]
        $( #[doc $($doc_attr)*] )*
        ///
        /// Accessor.
        /// For testing the logic which generates channel padding control instructions.
        pub fn $field(&self) -> Option<&$ty> {
            self.$field.as_ref()
        }
      )*
    }
} }

define_channels_insns_and_automatic_impls! {
    /// Whether to send padding
    padding_enable: bool,

    /// Padding timing parameters
    ///
    /// This is in abeyance if `send_padding` is `false`;
    /// we still pass it because the usual case is that padding is enabled/disabled
    /// rather than the parameters changing,
    /// so the padding timer always keeps parameters, even when disabled.
    //
    // The initial configuration of the padding timer used by the reactor has no
    // parameters, so does not send padding.  We need to mirror that here, so that we
    // give the reactor an initial set of timing parameters.
    #[field educe(Default(expression = "padding::Parameters::disabled()"))]
    padding_parameters: padding::Parameters,

    /// Channel padding negotiation cell
    ///
    /// In `ChannelPaddingInstructions`, and when set via `Builder`,
    /// this is the `PADDING_NEGOTIATE` cell which should be used when we want
    /// to instruct our peer (the guard) to do padding like we have concluded we want.
    ///
    /// (An initial `PaddingNegotiate::start_default()` is elided
    /// in [`Channel::engage_padding_activities`]
    /// since that is what the peer would do anyway.)
    ///
    /// [`Channel::engage_padding_activities`]: super::Channel::engage_padding_activities
    padding_negotiate: PaddingNegotiate,
}

/// Builder for a channels padding instructions update
///
/// Obtain this from `ChannelPaddingInstructions::update`,
/// call zero or more setter methods,
/// call [`finish`](ChannelPaddingInstructionsUpdatesBuilder::finish),
/// and then send the resulting message.
///
/// # Panics
///
/// Panics if dropped.  Instead, call `finish`.
pub struct ChannelPaddingInstructionsUpdatesBuilder<'c> {
    /// Tracking the existing instructions
    insns: &'c mut ChannelPaddingInstructions,

    /// The update we are building
    ///
    /// `None` means nothing has changed yet.
    update: Option<ChannelPaddingInstructionsUpdates>,

    /// Make it hard to write code paths that drop this
    drop_bomb: bool,
}

impl ChannelPaddingInstructions {
    /// Start building an update to channel padding instructions
    ///
    /// The builder **must not be dropped**, once created;
    /// instead, [`finish`](ChannelPaddingInstructionsUpdatesBuilder::finish) must be called.
    /// So prepare your new values first, perhaps fallibly,
    /// and only then create and use the builder and send the update, infallibly.
    ///
    /// (This is because the builder uses `self: ChannelPaddingInstructions`
    /// to track which values have changed,
    /// and the values in `self` are updated immediately by the field update methods.)
    ///
    /// # Panics
    ///
    /// [`ChannelPaddingInstructionsUpdatesBuilder`] panics if it is dropped.
    pub fn start_update(&mut self) -> ChannelPaddingInstructionsUpdatesBuilder {
        ChannelPaddingInstructionsUpdatesBuilder {
            insns: self,
            update: None,
            drop_bomb: true,
        }
    }
}

impl<'c> Drop for ChannelPaddingInstructionsUpdatesBuilder<'c> {
    fn drop(&mut self) {
        assert!(
            !self.drop_bomb,
            "ChannelPaddingInstructionsUpdatesBuilder dropped"
        );
    }
}

impl<'c> ChannelPaddingInstructionsUpdatesBuilder<'c> {
    /// Finalise the update
    ///
    /// If nothing actually changed, returns `None`.
    /// (Tracking this, and returning `None`, allows us to avoid bothering
    /// every channel with a null update.)
    ///
    /// If `Some` is returned, the update **must** be implemented,
    /// since the underlying tracking [`ChannelPaddingInstructions`] has already been updated.
    #[must_use = "the update from finish() must be sent, to avoid losing insns changes"]
    pub fn finish(mut self) -> Option<ChannelPaddingInstructionsUpdates> {
        self.drop_bomb = false;
        self.update.take()
    }
}