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
///! Types and implementations related to L2TP protocol messages.

#[cfg(test)]
mod tests;

pub mod avp;

mod control_message;
pub use control_message::ControlMessage;

mod data_message;
pub use data_message::DataMessage;

mod flags;
use flags::{Flags, MessageFlagType};

use crate::common::{Reader, ResultStr, Writer};

/// # Summary
/// A `Message` is a representation of an L2TP protocol message. It can be either a `DataMessage`
/// or a `ControlMessage` and constitutes the outermost container for the protocol.
#[derive(Debug, Eq, PartialEq)]
pub enum Message<'a> {
    Control(ControlMessage),
    Data(DataMessage<'a>),
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ValidateReserved {
    Yes,
    No,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ValidateVersion {
    Yes,
    No,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ValidateUnused {
    Yes,
    No,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ValidationOptions {
    pub reserved: ValidateReserved,
    pub version: ValidateVersion,
    pub unused: ValidateUnused,
}

impl<'a> Message<'a> {
    const PROTOCOL_VERSION: u8 = 2;

    /// # Summary
    /// Attempt to read a `Message` using a `Reader`.
    ///
    /// Note: Only validation of the protocol version will take place.
    #[inline]
    pub fn try_read<'b>(reader: &'b mut impl Reader<'a>) -> ResultStr<Self> {
        Self::try_read_validate(
            reader,
            ValidationOptions {
                reserved: ValidateReserved::No,
                version: ValidateVersion::Yes,
                unused: ValidateUnused::No,
            },
        )
    }

    /// # Summary
    /// Attempt to read a `Message` using a `Reader`. User-supplied `ValidationOptions` offer a way to ignore certain protocol mandates.
    #[inline]
    pub fn try_read_validate<'b>(
        reader: &'b mut impl Reader<'a>,
        validation_options: ValidationOptions,
    ) -> ResultStr<Self> {
        let flags = Flags::read(reader)?;

        if let ValidateVersion::Yes = validation_options.version {
            let version = flags.get_version();
            if version != Self::PROTOCOL_VERSION {
                return Err("Invalid version encountered");
            }
        }

        if let ValidateReserved::Yes = validation_options.reserved {
            if !flags.reserved_bits_ok() {
                return Err("Invalid reserved bits encountered");
            }
        }

        match flags.get_type() {
            MessageFlagType::Data => Ok(Message::Data(DataMessage::try_read(flags, reader)?)),
            MessageFlagType::Control => Ok(Message::Control(ControlMessage::try_read(
                flags,
                validation_options,
                reader,
            )?)),
        }
    }

    /// # Summary
    /// Write a `Message` using a mutable `Writer`.
    ///
    /// # Safety
    /// This function is marked as unsafe because the `Writer` trait offers no error handling mechanism.
    #[inline]
    pub unsafe fn write(&self, writer: &mut impl Writer) {
        match self {
            Message::Control(control) => control.write(Self::PROTOCOL_VERSION, writer),
            Message::Data(data) => data.write(Self::PROTOCOL_VERSION, writer),
        }
    }
}