toe_beans/v4/message/
encode.rs

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