toe_beans/v4/message/
message.rs

1use super::*;
2use mac_address::MacAddress;
3use std::net::Ipv4Addr;
4
5/// Wraps all the data sent between DHCP clients and servers.
6///
7/// Formally defined in [RFC-2131](https://datatracker.ietf.org/doc/html/rfc2131#section-2) as:
8///
9/// ```text
10///                     1                   2                   3
11/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
12/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13/// |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
14/// +---------------+---------------+---------------+---------------+
15/// |                            xid (4)                            |
16/// +-------------------------------+-------------------------------+
17/// |           secs (2)            |           flags (2)           |
18/// +-------------------------------+-------------------------------+
19/// |                          ciaddr  (4)                          |
20/// +---------------------------------------------------------------+
21/// |                          yiaddr  (4)                          |
22/// +---------------------------------------------------------------+
23/// |                          siaddr  (4)                          |
24/// +---------------------------------------------------------------+
25/// |                          giaddr  (4)                          |
26/// +---------------------------------------------------------------+
27/// |                          chaddr  (16)                         |
28/// +---------------------------------------------------------------+
29/// |                          sname   (64)                         |
30/// +---------------------------------------------------------------+
31/// |                          file    (128)                        |
32/// +---------------------------------------------------------------+
33/// |                          options (variable)                   |
34/// +---------------------------------------------------------------+
35/// ```
36#[derive(Debug, PartialEq)]
37pub struct Message {
38    /// Message type (not to be confused with a [MessageOptions]'s [MessageTypes])
39    pub op: Ops,
40    /// Hardware address type
41    pub htype: HTypes,
42    /// Hardware address length
43    pub hlen: u8,
44    /// Optionally used by relay agents when booting via a relay agent. Client sets to zero.
45    pub hops: u8,
46    /// The transaction id. A random number chosen by the client. Used by the client and server to associate messages and responses between a client and a server.
47    pub xid: u32,
48    /// Seconds elapsed since client started address acquisition/renewal process. Filled in by client.
49    pub secs: u16,
50    /// The leftmost bit is defined as the Broadcast flag. The remaining bits are reserved for future use, and must be set to zero by clients and ignored by servers and relay agents.
51    pub flags: Flags,
52    /// Client IP address. Only filled in if the client is in BOUND, RENEW, or REBINDING state and can respond to ARP requests.
53    pub ciaddr: Ipv4Addr,
54    /// Your IP address
55    pub yiaddr: Ipv4Addr,
56    /// IP address of the next server to use in bootstrap. Returned in DHCPOFFER, DHCPACK by server.
57    pub siaddr: Ipv4Addr,
58    /// Relay agent IP address. Used in booting via a relay agent.
59    pub giaddr: Ipv4Addr,
60    /// Client hardware address
61    ///
62    /// If using an ethernet hardware type you can get the value for this field with the [mac_address](https://crates.io/crates/mac_address) crate. A [Client](crate::v4::client::Client) will automatically use mac_address.
63    pub chaddr: [u8; 16],
64    /// Server host name (optional). Null terminated string. 64 bytes in length.
65    pub sname: SName,
66    /// Boot file name. Null terminated string. Generic name or null in DHCPDISCOVER. Fully qualified directory path name in DHCPOFFER. 128 bytes in length.
67    pub file: File,
68    /// The first four octets of options field with values 99, 130, 83, 99
69    pub magic: [u8; 4],
70    /// A variable length field with a minimum of 312 octets.
71    /// Options use [Tag-Length-Value](https://en.wikipedia.org/wiki/Type-length-value) (TLV) encoding, where multi-byte quantities are in network byte order.
72    /// The last option must be MessageOptions::End
73    pub options: Vec<MessageOptions>,
74}
75
76/// The first four octets of options field with values 99, 130, 83, 99
77pub const MAGIC: [u8; 4] = [99, 130, 83, 99];
78
79impl Encodable for Message {}
80impl Decodable for Message {}
81
82impl MessageHelpers for Message {
83    // TODO consider utilizing a HashMap like data structure here.
84    fn find_option(&self, tag: u8) -> Option<&MessageOptions> {
85        self.options.iter().find(|&option| option.to_tag() == tag)
86    }
87
88    fn add_option(&mut self, option: MessageOptions) {
89        self.options.push(option);
90    }
91
92    fn get_mac_address(&self) -> MacAddress {
93        MacAddress::new([
94            self.chaddr[0],
95            self.chaddr[1],
96            self.chaddr[2],
97            self.chaddr[3],
98            self.chaddr[4],
99            self.chaddr[5],
100        ])
101    }
102}
103
104impl DecodeMessage for Message {
105    type Op = Ops;
106    type Htype = HTypes;
107    type Hlen = u8;
108    type Hops = u8;
109    type Xid = u32;
110    type Secs = u16;
111    type Flags = Flags;
112    type Ciaddr = Ipv4Addr;
113    type Yiaddr = Ipv4Addr;
114    type Siaddr = Ipv4Addr;
115    type Giaddr = Ipv4Addr;
116    type Chaddr = [u8; 16];
117    type Sname = SName;
118    type File = File;
119    type Magic = [u8; 4];
120    type Options = Vec<MessageOptions>;
121
122    type Output = Self;
123
124    fn decode_op(undecoded: &UndecodedMessage) -> Self::Op {
125        Self::Op::from(undecoded.slice_op())
126    }
127
128    fn decode_htype(undecoded: &UndecodedMessage) -> Self::Htype {
129        Self::Htype::from(undecoded.slice_htype())
130    }
131
132    fn decode_hlen(undecoded: &UndecodedMessage) -> Self::Hlen {
133        undecoded.slice_hlen()
134    }
135
136    fn decode_hops(undecoded: &UndecodedMessage) -> Self::Hops {
137        undecoded.slice_hops()
138    }
139
140    fn decode_xid(undecoded: &UndecodedMessage) -> Self::Xid {
141        let bytes = undecoded.slice_xid();
142        u32::from_be_bytes(bytes)
143    }
144
145    fn decode_secs(undecoded: &UndecodedMessage) -> Self::Secs {
146        let bytes = undecoded.slice_secs();
147        u16::from_be_bytes(bytes)
148    }
149
150    fn decode_flags(undecoded: &UndecodedMessage) -> Self::Flags {
151        let byte = undecoded.slice_flags_temporary();
152        let broadcast = byte & 0b10000000 == 0b10000000;
153        Flags { broadcast }
154    }
155
156    fn decode_ciaddr(undecoded: &UndecodedMessage) -> Self::Ciaddr {
157        let bytes = undecoded.slice_ciaddr();
158        Ipv4Addr::from(bytes)
159    }
160
161    fn decode_yiaddr(undecoded: &UndecodedMessage) -> Self::Yiaddr {
162        let bytes = undecoded.slice_yiaddr();
163        Ipv4Addr::from(bytes)
164    }
165
166    fn decode_siaddr(undecoded: &UndecodedMessage) -> Self::Siaddr {
167        let bytes = undecoded.slice_siaddr();
168        Ipv4Addr::from(bytes)
169    }
170
171    fn decode_giaddr(undecoded: &UndecodedMessage) -> Self::Giaddr {
172        let bytes = undecoded.slice_giaddr();
173        Ipv4Addr::from(bytes)
174    }
175
176    fn decode_chaddr(undecoded: &UndecodedMessage) -> Self::Chaddr {
177        undecoded.slice_chaddr()
178    }
179
180    fn decode_sname(undecoded: &UndecodedMessage) -> Self::Sname {
181        SName::from_array(undecoded.slice_sname())
182    }
183
184    fn decode_file(undecoded: &UndecodedMessage) -> Self::File {
185        File::from_array(undecoded.slice_file())
186    }
187
188    fn decode_magic(_magic: &UndecodedMessage) -> Self::Magic {
189        MAGIC
190    }
191
192    fn decode_options(undecoded: &UndecodedMessage) -> Self::Options {
193        MessageOptions::from_bytes(undecoded.slice_options())
194    }
195
196    fn from_bytes(undecoded: &UndecodedMessage) -> Self::Output {
197        Message {
198            op: Self::decode_op(&undecoded),
199            htype: Self::decode_htype(&undecoded),
200            hlen: Self::decode_hlen(&undecoded),
201            hops: Self::decode_hops(&undecoded),
202            xid: Self::decode_xid(&undecoded),
203            secs: Self::decode_secs(&undecoded),
204            flags: Self::decode_flags(&undecoded),
205            ciaddr: Self::decode_ciaddr(&undecoded),
206            yiaddr: Self::decode_yiaddr(&undecoded),
207            siaddr: Self::decode_siaddr(&undecoded),
208            giaddr: Self::decode_giaddr(&undecoded),
209            chaddr: Self::decode_chaddr(&undecoded),
210            sname: Self::decode_sname(&undecoded),
211            file: Self::decode_file(&undecoded),
212            magic: Self::decode_magic(&undecoded),
213            options: Self::decode_options(&undecoded),
214        }
215    }
216}
217
218impl EncodeMessage for Message {
219    #[inline]
220    fn encode_op(&self) -> u8 {
221        (&self.op).into()
222    }
223
224    #[inline]
225    fn encode_htype(&self) -> u8 {
226        (&self.htype).into()
227    }
228
229    #[inline]
230    fn encode_hlen(&self) -> u8 {
231        self.hlen
232    }
233
234    #[inline]
235    fn encode_hops(&self) -> u8 {
236        self.hops
237    }
238
239    #[inline]
240    fn encode_xid(&self) -> [u8; 4] {
241        self.xid.to_be_bytes()
242    }
243
244    #[inline]
245    fn encode_secs(&self) -> [u8; 2] {
246        self.secs.to_be_bytes()
247    }
248
249    #[inline]
250    fn encode_flags(&self) -> [u8; 2] {
251        self.flags.temporary_to_bytes()
252    }
253
254    #[inline]
255    fn encode_ciaddr(&self) -> [u8; 4] {
256        self.ciaddr.octets()
257    }
258
259    #[inline]
260    fn encode_yiaddr(&self) -> [u8; 4] {
261        self.yiaddr.octets()
262    }
263
264    #[inline]
265    fn encode_siaddr(&self) -> [u8; 4] {
266        self.siaddr.octets()
267    }
268
269    #[inline]
270    fn encode_giaddr(&self) -> [u8; 4] {
271        self.giaddr.octets()
272    }
273
274    #[inline]
275    fn encode_chaddr(&self) -> [u8; 16] {
276        self.chaddr
277    }
278
279    #[inline]
280    fn encode_sname(&self) -> [u8; 64] {
281        (&self.sname).into()
282    }
283
284    #[inline]
285    fn encode_file(&self) -> [u8; 128] {
286        (&self.file).into()
287    }
288
289    #[inline]
290    fn encode_options(&self) -> Vec<u8> {
291        let mut encoded = Vec::with_capacity(self.options.len() * 2);
292
293        self.options
294            .iter()
295            .for_each(|option| option.extend_into(&mut encoded));
296
297        encoded
298    }
299}