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: Vec<MessageOptions>,
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.iter().find(|&option| option.to_tag() == tag)
83    }
84
85    fn add_option(&mut self, option: MessageOptions) {
86        self.options.push(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 = Vec<MessageOptions>;
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        let byte = undecoded.slice_flags_temporary();
149        let broadcast = byte & 0b10000000 == 0b10000000;
150        Flags { broadcast }
151    }
152
153    fn decode_ciaddr(undecoded: &UndecodedMessage) -> Self::Ciaddr {
154        let bytes = undecoded.slice_ciaddr();
155        Ipv4Addr::from(bytes)
156    }
157
158    fn decode_yiaddr(undecoded: &UndecodedMessage) -> Self::Yiaddr {
159        let bytes = undecoded.slice_yiaddr();
160        Ipv4Addr::from(bytes)
161    }
162
163    fn decode_siaddr(undecoded: &UndecodedMessage) -> Self::Siaddr {
164        let bytes = undecoded.slice_siaddr();
165        Ipv4Addr::from(bytes)
166    }
167
168    fn decode_giaddr(undecoded: &UndecodedMessage) -> Self::Giaddr {
169        let bytes = undecoded.slice_giaddr();
170        Ipv4Addr::from(bytes)
171    }
172
173    fn decode_chaddr(undecoded: &UndecodedMessage) -> Self::Chaddr {
174        undecoded.slice_chaddr()
175    }
176
177    fn decode_sname(undecoded: &UndecodedMessage) -> Self::Sname {
178        SName::from_array(undecoded.slice_sname())
179    }
180
181    fn decode_file(undecoded: &UndecodedMessage) -> Self::File {
182        File::from_array(undecoded.slice_file())
183    }
184
185    fn decode_magic(_magic: &UndecodedMessage) -> Self::Magic {
186        MAGIC
187    }
188
189    fn decode_options(undecoded: &UndecodedMessage) -> Self::Options {
190        MessageOptions::from_bytes(&undecoded.slice_options())
191    }
192
193    fn from_bytes(undecoded: &UndecodedMessage) -> Self::Output {
194        Message {
195            op: Self::decode_op(undecoded),
196            htype: Self::decode_htype(undecoded),
197            hlen: Self::decode_hlen(undecoded),
198            hops: Self::decode_hops(undecoded),
199            xid: Self::decode_xid(undecoded),
200            secs: Self::decode_secs(undecoded),
201            flags: Self::decode_flags(undecoded),
202            ciaddr: Self::decode_ciaddr(undecoded),
203            yiaddr: Self::decode_yiaddr(undecoded),
204            siaddr: Self::decode_siaddr(undecoded),
205            giaddr: Self::decode_giaddr(undecoded),
206            chaddr: Self::decode_chaddr(undecoded),
207            sname: Self::decode_sname(undecoded),
208            file: Self::decode_file(undecoded),
209            magic: Self::decode_magic(undecoded),
210            options: Self::decode_options(undecoded),
211        }
212    }
213}
214
215impl EncodeMessage for Message {
216    #[inline]
217    fn encode_op(&self) -> u8 {
218        (&self.op).into()
219    }
220
221    #[inline]
222    fn encode_htype(&self) -> u8 {
223        (&self.htype).into()
224    }
225
226    #[inline]
227    fn encode_hlen(&self) -> u8 {
228        self.hlen
229    }
230
231    #[inline]
232    fn encode_hops(&self) -> u8 {
233        self.hops
234    }
235
236    #[inline]
237    fn encode_xid(&self) -> [u8; 4] {
238        self.xid.to_be_bytes()
239    }
240
241    #[inline]
242    fn encode_secs(&self) -> [u8; 2] {
243        self.secs.to_be_bytes()
244    }
245
246    #[inline]
247    fn encode_flags(&self) -> [u8; 2] {
248        self.flags.temporary_to_bytes()
249    }
250
251    #[inline]
252    fn encode_ciaddr(&self) -> [u8; 4] {
253        self.ciaddr.octets()
254    }
255
256    #[inline]
257    fn encode_yiaddr(&self) -> [u8; 4] {
258        self.yiaddr.octets()
259    }
260
261    #[inline]
262    fn encode_siaddr(&self) -> [u8; 4] {
263        self.siaddr.octets()
264    }
265
266    #[inline]
267    fn encode_giaddr(&self) -> [u8; 4] {
268        self.giaddr.octets()
269    }
270
271    #[inline]
272    fn encode_chaddr(&self) -> [u8; 16] {
273        self.chaddr
274    }
275
276    #[inline]
277    fn encode_sname(&self) -> [u8; 64] {
278        (&self.sname).into()
279    }
280
281    #[inline]
282    fn encode_file(&self) -> [u8; 128] {
283        (&self.file).into()
284    }
285
286    #[inline]
287    fn encode_options(&self) -> Vec<u8> {
288        let mut encoded = Vec::with_capacity(MIN_OPTIONS_SIZE);
289
290        self.options
291            .iter()
292            .for_each(|option| option.extend_into(&mut encoded));
293
294        encoded
295    }
296}