1
//! Circuit padding
2
//!
3
// TODO(DEDUP): we should eventually move client::circuit::padding here
4

            
5
#[cfg(feature = "circ-padding")]
6
use {crate::circuit::cell_sender::CircuitCellSender, crate::client::circuit::padding};
7

            
8
/// A possible way to handle a request to send padding.
9
#[derive(Copy, Clone, Debug)]
10
pub(crate) enum CircPaddingDisposition {
11
    /// Enqueue the padding normally.
12
    QueuePaddingNormally,
13
    /// Enqueue the padding, and allow one cell of data on our outbound queue
14
    /// to bypass the current block.
15
    QueuePaddingAndBypass,
16
    /// Do not take any actual padding action:
17
    /// existing data on our outbound queue will count as padding.
18
    TreatQueuedCellAsPadding,
19
}
20

            
21
/// Determine how exactly to handle a request to handle padding.
22
///
23
/// This is fairly complicated; see the maybenot documentation for more information.
24
///
25
// TODO(relay): relays use the same logic as clients here. Is that okay,
26
// or do they need to handle SendPadding differently??
27
#[cfg(feature = "circ-padding")]
28
pub(crate) fn padding_disposition(
29
    send_padding: &padding::SendPadding,
30
    chan_sender: &CircuitCellSender,
31
    padding_block: Option<&padding::StartBlocking>,
32
) -> CircPaddingDisposition {
33
    use CircPaddingDisposition::*;
34
    use padding::Bypass::*;
35
    use padding::Replace::*;
36

            
37
    // If true, and we are trying to send Replaceable padding,
38
    // we should let any data in the queue count as the queued padding instead,
39
    // if it is queued for our target hop (or any subsequent hop).
40
    //
41
    // TODO circpad: In addition to letting currently-queued data count as padding,
42
    // maybenot also permits us to send currently pending data from our streams
43
    // (or from our next hop, if we're a relay).  We don't have that implemented yet.
44
    //
45
    // TODO circpad: This will usually be false, since we try not to queue data
46
    // when there isn't space to write it.  If we someday add internal per-circuit
47
    // Buffers to chan_sender, this test is more likely to trigger.
48
    let have_queued_cell_for_hop = chan_sender.have_queued_cell_for_hop_or_later(send_padding.hop);
49

            
50
    match padding_block {
51
        Some(blocking) if blocking.is_bypassable => {
52
            match (
53
                send_padding.may_replace_with_data(),
54
                send_padding.may_bypass_block(),
55
            ) {
56
                (NotReplaceable, DoNotBypass) => QueuePaddingNormally,
57
                (NotReplaceable, BypassBlocking) => QueuePaddingAndBypass,
58
                (Replaceable, DoNotBypass) => {
59
                    if have_queued_cell_for_hop {
60
                        TreatQueuedCellAsPadding
61
                    } else {
62
                        QueuePaddingNormally
63
                    }
64
                }
65
                (Replaceable, BypassBlocking) => {
66
                    if have_queued_cell_for_hop {
67
                        TreatQueuedCellAsPadding
68
                    } else {
69
                        QueuePaddingAndBypass
70
                    }
71
                }
72
            }
73
        }
74
        Some(_) | None => match send_padding.may_replace_with_data() {
75
            Replaceable => {
76
                if have_queued_cell_for_hop {
77
                    TreatQueuedCellAsPadding
78
                } else {
79
                    QueuePaddingNormally
80
                }
81
            }
82
            NotReplaceable => QueuePaddingNormally,
83
        },
84
    }
85
}