toe_beans/v4/message/
message.rs

1use super::*;
2use crate::v4::error::Result;
3use mac_address::MacAddress;
4use std::net::Ipv4Addr;
5
6/// Wraps all the data sent between DHCP clients and servers.
7///
8/// Formally defined in [RFC-2131](https://datatracker.ietf.org/doc/html/rfc2131#section-2) as:
9///
10/// ```text
11///                     1                   2                   3
12/// 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
13/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14/// |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
15/// +---------------+---------------+---------------+---------------+
16/// |                            xid (4)                            |
17/// +-------------------------------+-------------------------------+
18/// |           secs (2)            |           flags (2)           |
19/// +-------------------------------+-------------------------------+
20/// |                          ciaddr  (4)                          |
21/// +---------------------------------------------------------------+
22/// |                          yiaddr  (4)                          |
23/// +---------------------------------------------------------------+
24/// |                          siaddr  (4)                          |
25/// +---------------------------------------------------------------+
26/// |                          giaddr  (4)                          |
27/// +---------------------------------------------------------------+
28/// |                          chaddr  (16)                         |
29/// +---------------------------------------------------------------+
30/// |                          sname   (64)                         |
31/// +---------------------------------------------------------------+
32/// |                          file    (128)                        |
33/// +---------------------------------------------------------------+
34/// |                          options (variable)                   |
35/// +---------------------------------------------------------------+
36/// ```
37#[derive(Debug, PartialEq)]
38pub struct Message {
39    /// Message type (not to be confused with a [MessageOptions]'s [MessageTypes])
40    pub op: Ops,
41    /// Hardware address type
42    pub htype: HTypes,
43    /// Hardware address length
44    pub hlen: u8,
45    /// Optionally used by relay agents when booting via a relay agent. Client sets to zero.
46    pub hops: u8,
47    /// 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.
48    pub xid: u32,
49    /// Seconds elapsed since client started address acquisition/renewal process. Filled in by client.
50    pub secs: u16,
51    /// 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.
52    pub flags: Flags,
53    /// Client IP address. Only filled in if the client is in BOUND, RENEW, or REBINDING state and can respond to ARP requests.
54    pub ciaddr: Ipv4Addr,
55    /// Your IP address
56    pub yiaddr: Ipv4Addr,
57    /// IP address of the next server to use in bootstrap. Returned in DHCPOFFER, DHCPACK by server.
58    pub siaddr: Ipv4Addr,
59    /// Relay agent IP address. Used in booting via a relay agent.
60    pub giaddr: Ipv4Addr,
61    /// Client hardware address
62    ///
63    /// 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.
64    pub chaddr: [u8; 16],
65    /// Server host name (optional). Null terminated string. 64 bytes in length.
66    pub sname: SName,
67    /// Boot file name. Null terminated string. Generic name or null in DHCPDISCOVER. Fully qualified directory path name in DHCPOFFER. 128 bytes in length.
68    pub file: File,
69    /// The first four octets of options field with values 99, 130, 83, 99
70    pub magic: [u8; 4],
71    /// A variable length field with a minimum of 312 octets.
72    /// Options use [Tag-Length-Value](https://en.wikipedia.org/wiki/Type-length-value) (TLV) encoding, where multi-byte quantities are in network byte order.
73    /// The last option must be MessageOptions::End
74    pub options: Vec<MessageOptions>,
75}
76
77/// The first four octets of options field with values 99, 130, 83, 99
78pub const MAGIC: [u8; 4] = [99, 130, 83, 99];
79
80impl Deliverable 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.insert(self.options.len() - 1, 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(op: &[u8]) -> Self::Op {
125        Self::Op::from(op)
126    }
127
128    fn decode_htype(htype: &[u8]) -> Self::Htype {
129        Self::Htype::from(htype)
130    }
131
132    fn decode_hlen(hlen: &[u8]) -> Self::Hlen {
133        hlen[0]
134    }
135
136    fn decode_hops(hops: &[u8]) -> Self::Hops {
137        hops[0]
138    }
139
140    fn decode_xid(xid: &[u8]) -> Self::Xid {
141        let bytes: [u8; 4] = xid.try_into().unwrap();
142        u32::from_be_bytes(bytes)
143    }
144
145    fn decode_secs(secs: &[u8]) -> Self::Secs {
146        let bytes: [u8; 2] = secs.try_into().unwrap();
147        u16::from_be_bytes(bytes)
148    }
149
150    fn decode_flags(flags: &[u8]) -> Self::Flags {
151        let byte = flags[0];
152        let broadcast = byte & 0b10000000 == 0b10000000;
153        Flags { broadcast }
154    }
155
156    fn decode_ciaddr(ciaddr: &[u8]) -> Self::Ciaddr {
157        let bytes: [u8; 4] = ciaddr.try_into().unwrap();
158        Ipv4Addr::from(bytes)
159    }
160
161    fn decode_yiaddr(yiaddr: &[u8]) -> Self::Yiaddr {
162        let bytes: [u8; 4] = yiaddr.try_into().unwrap();
163        Ipv4Addr::from(bytes)
164    }
165
166    fn decode_siaddr(siaddr: &[u8]) -> Self::Siaddr {
167        let bytes: [u8; 4] = siaddr.try_into().unwrap();
168        Ipv4Addr::from(bytes)
169    }
170
171    fn decode_giaddr(giaddr: &[u8]) -> Self::Giaddr {
172        let bytes: [u8; 4] = giaddr.try_into().unwrap();
173        Ipv4Addr::from(bytes)
174    }
175
176    fn decode_chaddr(chaddr: &[u8]) -> Self::Chaddr {
177        let bytes: [u8; 16] = chaddr.try_into().unwrap();
178        bytes
179    }
180
181    fn decode_sname(sname: &[u8]) -> Self::Sname {
182        SName::from_slice_unchecked(sname)
183    }
184
185    fn decode_file(file: &[u8]) -> Self::File {
186        File::from_slice_unchecked(file)
187    }
188
189    fn decode_magic(_magic: &[u8]) -> Self::Magic {
190        MAGIC
191    }
192
193    fn decode_options(options: &[u8]) -> Self::Options {
194        MessageOptions::from_bytes(options)
195    }
196
197    fn from_bytes(input: &[u8]) -> Result<Self::Output> {
198        let slicer = MessageSlicer::new(input)?;
199
200        Ok(Message {
201            op: Self::decode_op(slicer.parse_op()),
202            htype: Self::decode_htype(slicer.parse_htype()),
203            hlen: Self::decode_hlen(slicer.parse_hlen()),
204            hops: Self::decode_hops(slicer.parse_hops()),
205            xid: Self::decode_xid(slicer.parse_xid()),
206            secs: Self::decode_secs(slicer.parse_secs()),
207            flags: Self::decode_flags(slicer.parse_flags()),
208            ciaddr: Self::decode_ciaddr(slicer.parse_ciaddr()),
209            yiaddr: Self::decode_yiaddr(slicer.parse_yiaddr()),
210            siaddr: Self::decode_siaddr(slicer.parse_siaddr()),
211            giaddr: Self::decode_giaddr(slicer.parse_giaddr()),
212            chaddr: Self::decode_chaddr(slicer.parse_chaddr()),
213            sname: Self::decode_sname(slicer.parse_sname()),
214            file: Self::decode_file(slicer.parse_file()),
215            magic: Self::decode_magic(slicer.parse_magic()),
216            options: Self::decode_options(slicer.parse_options()),
217        })
218    }
219}
220
221impl EncodeMessage for Message {
222    #[inline]
223    fn encode_op(&self) -> u8 {
224        (&self.op).into()
225    }
226
227    #[inline]
228    fn encode_htype(&self) -> u8 {
229        (&self.htype).into()
230    }
231
232    #[inline]
233    fn encode_hlen(&self) -> u8 {
234        self.hlen
235    }
236
237    #[inline]
238    fn encode_hops(&self) -> u8 {
239        self.hops
240    }
241
242    #[inline]
243    fn encode_xid(&self) -> [u8; 4] {
244        self.xid.to_be_bytes()
245    }
246
247    #[inline]
248    fn encode_secs(&self) -> [u8; 2] {
249        self.secs.to_be_bytes()
250    }
251
252    #[inline]
253    fn encode_flags(&self) -> [u8; 2] {
254        (&self.flags).temporary_to_bytes()
255    }
256
257    #[inline]
258    fn encode_ciaddr(&self) -> [u8; 4] {
259        self.ciaddr.octets()
260    }
261
262    #[inline]
263    fn encode_yiaddr(&self) -> [u8; 4] {
264        self.yiaddr.octets()
265    }
266
267    #[inline]
268    fn encode_siaddr(&self) -> [u8; 4] {
269        self.siaddr.octets()
270    }
271
272    #[inline]
273    fn encode_giaddr(&self) -> [u8; 4] {
274        self.giaddr.octets()
275    }
276
277    #[inline]
278    fn encode_chaddr(&self) -> [u8; 16] {
279        self.chaddr
280    }
281
282    #[inline]
283    fn encode_sname(&self) -> [u8; 64] {
284        (&self.sname).into()
285    }
286
287    #[inline]
288    fn encode_file(&self) -> [u8; 128] {
289        (&self.file).into()
290    }
291
292    #[inline]
293    fn encode_options(&self) -> Vec<u8> {
294        let mut encoded = Vec::with_capacity(self.options.len() * 2);
295
296        self.options
297            .iter()
298            .for_each(|option| option.extend_into(&mut encoded));
299
300        encoded
301    }
302}