Skip to main content

toe_beans/v4/message/
encode.rs

1use super::MAGIC;
2use crate::v4::{MAX_MESSAGE_SIZE, MagicBuffer};
3use log::trace;
4use std::fmt::Debug;
5
6/// A supertrait that defines everything a custom message
7/// needs to be encoded before being sent by a socket.
8pub trait Encodable: EncodeMessage + Debug {}
9
10/// Define how your custom Message type encodes
11/// itself into the bytes of a DHCP message's fields.
12///
13/// ...or don't and use our homemade [Message](crate::v4::message::Message)
14/// with this already implemented.
15///
16/// If your type is in a different module you can define
17/// [From or Into](https://doc.rust-lang.org/std/convert/index.html)
18/// in that module and call that method in one of this trait's `encode_*`
19/// methods. See Message's implementation for details.
20pub trait EncodeMessage {
21    /// Represent any data type as the correct amount of op field bytes.
22    fn encode_op(&self) -> u8;
23    /// Represent any data type as the correct amount of htype field bytes.
24    fn encode_htype(&self) -> u8;
25    /// Represent any data type as the correct amount of hlen field bytes.
26    fn encode_hlen(&self) -> u8;
27    /// Represent any data type as the correct amount of hops field bytes.
28    fn encode_hops(&self) -> u8;
29    /// Represent any data type as the correct amount of xid field bytes.
30    fn encode_xid(&self) -> [u8; 4];
31    /// Represent any data type as the correct amount of secs field bytes.
32    fn encode_secs(&self) -> [u8; 2];
33    /// Represent any data type as the correct amount of flags field bytes.
34    fn encode_flags(&self) -> [u8; 2];
35    /// Represent any data type as the correct amount of ciaddr field bytes.
36    fn encode_ciaddr(&self) -> [u8; 4];
37    /// Represent any data type as the correct amount of yiaddr field bytes.
38    fn encode_yiaddr(&self) -> [u8; 4];
39    /// Represent any data type as the correct amount of siaddr field bytes.
40    fn encode_siaddr(&self) -> [u8; 4];
41    /// Represent any data type as the correct amount of giaddr field bytes.
42    fn encode_giaddr(&self) -> [u8; 4];
43    /// Represent any data type as the correct amount of chaddr field bytes.
44    fn encode_chaddr(&self) -> [u8; 16];
45    /// Represent any data type as the correct amount of sname field bytes.
46    fn encode_sname(&self) -> [u8; 64];
47    /// Represent any data type as the correct amount of file field bytes.
48    fn encode_file(&self) -> [u8; 128];
49    /// Represent any data type as a variable amount of options field bytes.
50    fn encode_options(&self) -> Vec<u8>;
51
52    /// Automagically defined because its a constant
53    fn encode_magic(&self) -> MagicBuffer {
54        MAGIC
55    }
56
57    /// Encodes a [Message](crate::v4::Message) into a Vec of bytes
58    /// by calling the encode_* methods defined in EncodeMessage,
59    /// and uses a Encoder to store the output.
60    fn to_bytes(&self) -> Vec<u8> {
61        trace!("to_bytes");
62
63        let mut encoder = Encoder {
64            output: Vec::with_capacity(MAX_MESSAGE_SIZE),
65        };
66
67        trace!("encode op");
68        encoder.encode_byte(self.encode_op());
69        trace!("encode htype");
70        encoder.encode_byte(self.encode_htype());
71        trace!("encode hlen");
72        encoder.encode_byte(self.encode_hlen());
73        trace!("encode hops");
74        encoder.encode_byte(self.encode_hops());
75        trace!("encode xid");
76        encoder.encode_bytes(&self.encode_xid());
77        trace!("encode secs");
78        encoder.encode_bytes(&self.encode_secs());
79        trace!("encode flags");
80        encoder.encode_bytes(&self.encode_flags());
81        trace!("encode ciaddr");
82        encoder.encode_bytes(&self.encode_ciaddr());
83        trace!("encode yiaddr");
84        encoder.encode_bytes(&self.encode_yiaddr());
85        trace!("encode siaddr");
86        encoder.encode_bytes(&self.encode_siaddr());
87        trace!("encode giaddr");
88        encoder.encode_bytes(&self.encode_giaddr());
89        trace!("encode chaddr");
90        encoder.encode_bytes(&self.encode_chaddr());
91        trace!("encode sname");
92        encoder.encode_bytes(&self.encode_sname());
93        trace!("encode file");
94        encoder.encode_bytes(&self.encode_file());
95        trace!("encode magic");
96        encoder.encode_bytes(&self.encode_magic());
97        trace!("encode options");
98        encoder.encode_bytes(self.encode_options().as_slice());
99
100        // Automatically add the "End" option.
101        encoder.encode_byte(255);
102
103        encoder.output
104    }
105}
106
107/// Keeps track of the state of the output and provides methods to add data to the output.
108#[derive(Debug)]
109pub struct Encoder {
110    // This Vec starts empty and bytes are pushed as values are encoded.
111    output: Vec<u8>,
112}
113
114// Helpers for serializing in encode methods
115impl Encoder {
116    #[inline]
117    fn encode_bytes(&mut self, v: &[u8]) {
118        trace!("encode_bytes");
119        self.output.extend_from_slice(v);
120    }
121
122    #[inline]
123    fn encode_byte(&mut self, v: u8) {
124        trace!("encode_byte");
125        self.output.push(v);
126    }
127}
128
129// ---------------------------
130
131#[cfg(test)]
132mod tests {
133    use std::net::Ipv4Addr;
134
135    use super::*;
136    use crate::v4::*;
137
138    // Output additional debug info with
139    // RUST_LOG=debug cargo test test_to_bytes -- --show-output
140    fn init_logger() {
141        let _ = env_logger::builder().is_test(true).try_init();
142    }
143
144    #[test]
145    fn test_to_bytes() {
146        init_logger();
147
148        let message = Message {
149            op: Ops::Request,
150            htype: HTypes::Ethernet,
151            hlen: 6,
152            hops: 1,
153            xid: 4,
154            secs: 2,
155            flags: Flags { broadcast: true },
156            ciaddr: Ipv4Addr::new(1, 1, 1, 1),
157            yiaddr: Ipv4Addr::new(1, 1, 1, 1),
158            siaddr: Ipv4Addr::new(1, 1, 1, 1),
159            giaddr: Ipv4Addr::new(1, 1, 1, 1),
160            chaddr: [3; 16],
161            sname: SName::new(c"000000000000000000000000000000000000000000000000000000000000000").unwrap(),
162            file: File::new(c"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@").unwrap(),
163            magic: [99, 130, 83, 99],
164            options: vec![
165                MessageOptions::MessageType(MessageTypes::Discover),
166            ],
167        };
168
169        let expected = vec![
170            1, 1, 6, 1, 0, 0, 0, 4, 0, 2, 128, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
171            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
172            48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
173            48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
174            48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
175            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
176            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
177            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
178            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
179            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
180            64, 64, 64, 64, 64, 0, 99, 130, 83, 99, 53, 1, 1, 255,
181        ];
182
183        assert_eq!(message.to_bytes(), expected);
184    }
185}