Skip to main content

toe_beans/v4/message/
mod.rs

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