Skip to main content

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: MagicBuffer,
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: MessageOptionsList,
74}
75
76impl Encodable for Message {}
77impl Decodable for Message {}
78
79impl MessageHelpers for Message {
80    // TODO consider utilizing a HashMap like data structure here.
81    fn find_option(&self, tag: u8) -> Option<&MessageOptions> {
82        self.options.find_option(tag)
83    }
84
85    fn add_option(&mut self, option: MessageOptions) {
86        self.options.add_option(option);
87    }
88
89    fn get_mac_address(&self) -> MacAddress {
90        MacAddress::new([
91            self.chaddr[0],
92            self.chaddr[1],
93            self.chaddr[2],
94            self.chaddr[3],
95            self.chaddr[4],
96            self.chaddr[5],
97        ])
98    }
99}
100
101impl DecodeMessage for Message {
102    type Op = Ops;
103    type Htype = HTypes;
104    type Hlen = u8;
105    type Hops = u8;
106    type Xid = u32;
107    type Secs = u16;
108    type Flags = Flags;
109    type Ciaddr = Ipv4Addr;
110    type Yiaddr = Ipv4Addr;
111    type Siaddr = Ipv4Addr;
112    type Giaddr = Ipv4Addr;
113    type Chaddr = [u8; 16];
114    type Sname = SName;
115    type File = File;
116    type Magic = MagicBuffer;
117    type Options = MessageOptionsList;
118
119    type Output = Self;
120
121    fn decode_op(undecoded: &UndecodedMessage) -> Self::Op {
122        Self::Op::from(undecoded.slice_op())
123    }
124
125    fn decode_htype(undecoded: &UndecodedMessage) -> Self::Htype {
126        Self::Htype::from(undecoded.slice_htype())
127    }
128
129    fn decode_hlen(undecoded: &UndecodedMessage) -> Self::Hlen {
130        undecoded.slice_hlen()
131    }
132
133    fn decode_hops(undecoded: &UndecodedMessage) -> Self::Hops {
134        undecoded.slice_hops()
135    }
136
137    fn decode_xid(undecoded: &UndecodedMessage) -> Self::Xid {
138        let bytes = undecoded.slice_xid();
139        u32::from_be_bytes(bytes)
140    }
141
142    fn decode_secs(undecoded: &UndecodedMessage) -> Self::Secs {
143        let bytes = undecoded.slice_secs();
144        u16::from_be_bytes(bytes)
145    }
146
147    fn decode_flags(undecoded: &UndecodedMessage) -> Self::Flags {
148        Self::Flags::from(undecoded.slice_flags_temporary())
149    }
150
151    fn decode_ciaddr(undecoded: &UndecodedMessage) -> Self::Ciaddr {
152        let bytes = undecoded.slice_ciaddr();
153        Ipv4Addr::from(bytes)
154    }
155
156    fn decode_yiaddr(undecoded: &UndecodedMessage) -> Self::Yiaddr {
157        let bytes = undecoded.slice_yiaddr();
158        Ipv4Addr::from(bytes)
159    }
160
161    fn decode_siaddr(undecoded: &UndecodedMessage) -> Self::Siaddr {
162        let bytes = undecoded.slice_siaddr();
163        Ipv4Addr::from(bytes)
164    }
165
166    fn decode_giaddr(undecoded: &UndecodedMessage) -> Self::Giaddr {
167        let bytes = undecoded.slice_giaddr();
168        Ipv4Addr::from(bytes)
169    }
170
171    fn decode_chaddr(undecoded: &UndecodedMessage) -> Self::Chaddr {
172        undecoded.slice_chaddr()
173    }
174
175    fn decode_sname(undecoded: &UndecodedMessage) -> Self::Sname {
176        SName::from_array(undecoded.slice_sname())
177    }
178
179    fn decode_file(undecoded: &UndecodedMessage) -> Self::File {
180        File::from_array(undecoded.slice_file())
181    }
182
183    fn decode_magic(_magic: &UndecodedMessage) -> Self::Magic {
184        MAGIC
185    }
186
187    fn decode_options(undecoded: &UndecodedMessage) -> Self::Options {
188        MessageOptionsList::from_bytes(&undecoded.slice_options())
189    }
190
191    fn from_bytes(undecoded: &UndecodedMessage) -> Self::Output {
192        Message {
193            op: Self::decode_op(undecoded),
194            htype: Self::decode_htype(undecoded),
195            hlen: Self::decode_hlen(undecoded),
196            hops: Self::decode_hops(undecoded),
197            xid: Self::decode_xid(undecoded),
198            secs: Self::decode_secs(undecoded),
199            flags: Self::decode_flags(undecoded),
200            ciaddr: Self::decode_ciaddr(undecoded),
201            yiaddr: Self::decode_yiaddr(undecoded),
202            siaddr: Self::decode_siaddr(undecoded),
203            giaddr: Self::decode_giaddr(undecoded),
204            chaddr: Self::decode_chaddr(undecoded),
205            sname: Self::decode_sname(undecoded),
206            file: Self::decode_file(undecoded),
207            magic: Self::decode_magic(undecoded),
208            options: Self::decode_options(undecoded),
209        }
210    }
211}
212
213impl EncodeMessage for Message {
214    #[inline]
215    fn encode_op(&self) -> u8 {
216        (&self.op).into()
217    }
218
219    #[inline]
220    fn encode_htype(&self) -> u8 {
221        (&self.htype).into()
222    }
223
224    #[inline]
225    fn encode_hlen(&self) -> u8 {
226        self.hlen
227    }
228
229    #[inline]
230    fn encode_hops(&self) -> u8 {
231        self.hops
232    }
233
234    #[inline]
235    fn encode_xid(&self) -> [u8; 4] {
236        self.xid.to_be_bytes()
237    }
238
239    #[inline]
240    fn encode_secs(&self) -> [u8; 2] {
241        self.secs.to_be_bytes()
242    }
243
244    #[inline]
245    fn encode_flags(&self) -> [u8; 2] {
246        self.flags.temporary_to_bytes()
247    }
248
249    #[inline]
250    fn encode_ciaddr(&self) -> [u8; 4] {
251        self.ciaddr.octets()
252    }
253
254    #[inline]
255    fn encode_yiaddr(&self) -> [u8; 4] {
256        self.yiaddr.octets()
257    }
258
259    #[inline]
260    fn encode_siaddr(&self) -> [u8; 4] {
261        self.siaddr.octets()
262    }
263
264    #[inline]
265    fn encode_giaddr(&self) -> [u8; 4] {
266        self.giaddr.octets()
267    }
268
269    #[inline]
270    fn encode_chaddr(&self) -> [u8; 16] {
271        self.chaddr
272    }
273
274    #[inline]
275    fn encode_sname(&self) -> [u8; 64] {
276        (&self.sname).into()
277    }
278
279    #[inline]
280    fn encode_file(&self) -> [u8; 128] {
281        (&self.file).into()
282    }
283
284    #[inline]
285    fn encode_options(&self) -> Vec<u8> {
286        (&self.options).into()
287    }
288}