Skip to content

Commit c9f90ef

Browse files
committed
Implement more DHCP options
1 parent 7e8fe22 commit c9f90ef

File tree

3 files changed

+83
-61
lines changed

3 files changed

+83
-61
lines changed

crates/lib-dhcp/src/types/message.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ pub enum MessageError {
2222
#[error("Buffer error: {0}")]
2323
BufferError(#[from] BufferError),
2424

25-
#[error("Option with tag {0} already ppresent, duplicates are not allowed")]
25+
#[error("Option with tag {0} already present, duplicates are not allowed")]
2626
DuplicateOptionError(OptionTag),
27+
28+
#[error("No DHCP magic cookie found at the start of OPTIONS field")]
29+
NoMagicCookie,
2730
}
2831

2932
/// [`Message`] describes a complete DHCP message. The same packet field
@@ -142,8 +145,8 @@ impl Readable for Message {
142145

143146
match buf.peekn::<4>() {
144147
Some(m) if m == constants::MAGIC_COOKIE_ARR => buf.skipn(4)?,
145-
Some(_) => return Err(MessageError::BufferError(BufferError::InvalidData)),
146-
None => return Err(MessageError::BufferError(BufferError::BufTooShort)),
148+
Some(_) => return Err(MessageError::NoMagicCookie),
149+
None => return Err(BufferError::BufTooShort.into()),
147150
};
148151

149152
let options = read_options::<E>(buf)?;

crates/lib-dhcp/src/types/option/data.rs

+76-57
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ pub enum OptionData {
3434
Pad,
3535
End,
3636
SubnetMask(Ipv4Addr),
37-
TimeOffset,
37+
TimeOffset(u32),
3838
Router(Vec<Ipv4Addr>),
39-
TimeServer,
40-
NameServer,
39+
TimeServer(Vec<Ipv4Addr>),
40+
NameServer(Vec<Ipv4Addr>),
4141
DomainNameServer(Vec<Ipv4Addr>),
42-
LogServer,
43-
CookieServer,
44-
LprServer,
45-
ImpressServer,
46-
ResourceLocationServer,
42+
LogServer(Vec<Ipv4Addr>),
43+
CookieServer(Vec<Ipv4Addr>),
44+
LprServer(Vec<Ipv4Addr>),
45+
ImpressServer(Vec<Ipv4Addr>),
46+
ResourceLocationServer(Vec<Ipv4Addr>),
4747
HostName(String),
48-
BootFileSize,
48+
BootFileSize(u16),
4949
MeritDumpFile,
5050
DomainName,
5151
SwapServer,
@@ -166,18 +166,18 @@ impl Writeable for OptionData {
166166
OptionData::Pad => 0u8.write::<E>(buf)?,
167167
OptionData::End => 255u8.write::<E>(buf)?,
168168
OptionData::SubnetMask(mask) => mask.write::<E>(buf)?,
169-
OptionData::TimeOffset => todo!(),
169+
OptionData::TimeOffset(off) => off.write::<E>(buf)?,
170170
OptionData::Router(ips) => ips.write::<E>(buf)?,
171-
OptionData::TimeServer => todo!(),
172-
OptionData::NameServer => todo!(),
171+
OptionData::TimeServer(ips) => ips.write::<E>(buf)?,
172+
OptionData::NameServer(ips) => ips.write::<E>(buf)?,
173173
OptionData::DomainNameServer(ips) => ips.write::<E>(buf)?,
174-
OptionData::LogServer => todo!(),
175-
OptionData::CookieServer => todo!(),
176-
OptionData::LprServer => todo!(),
177-
OptionData::ImpressServer => todo!(),
178-
OptionData::ResourceLocationServer => todo!(),
174+
OptionData::LogServer(ips) => ips.write::<E>(buf)?,
175+
OptionData::CookieServer(ips) => ips.write::<E>(buf)?,
176+
OptionData::LprServer(ips) => ips.write::<E>(buf)?,
177+
OptionData::ImpressServer(ips) => ips.write::<E>(buf)?,
178+
OptionData::ResourceLocationServer(ips) => ips.write::<E>(buf)?,
179179
OptionData::HostName(name) => name.write::<E>(buf)?,
180-
OptionData::BootFileSize => todo!(),
180+
OptionData::BootFileSize(size) => size.write::<E>(buf)?,
181181
OptionData::MeritDumpFile => todo!(),
182182
OptionData::DomainName => todo!(),
183183
OptionData::SwapServer => todo!(),
@@ -241,39 +241,39 @@ impl OptionData {
241241
OptionTag::Pad => Self::Pad,
242242
OptionTag::End => Self::End,
243243
OptionTag::SubnetMask => Self::SubnetMask(Ipv4Addr::read::<E>(buf)?),
244-
OptionTag::TimeOffset => todo!(),
244+
OptionTag::TimeOffset => Self::TimeOffset(u32::read::<E>(buf)?),
245245
OptionTag::Router => {
246-
if header.len % 4 != 0 {
247-
return Err(OptionDataError::InvalidData);
248-
}
249-
250-
let mut ips = Vec::new();
251-
252-
for _ in 0..header.len / 4 {
253-
ips.push(Ipv4Addr::read::<E>(buf)?);
254-
}
255-
246+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
256247
Self::Router(ips)
257248
}
258-
OptionTag::TimeServer => todo!(),
259-
OptionTag::NameServer => todo!(),
249+
OptionTag::TimeServer => {
250+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
251+
Self::TimeServer(ips)
252+
}
253+
OptionTag::NameServer => {
254+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
255+
Self::NameServer(ips)
256+
}
260257
OptionTag::DomainNameServer => {
261-
if header.len % 4 != 0 {
262-
return Err(OptionDataError::InvalidData);
263-
}
264-
265-
let mut ips = Vec::new();
266-
267-
for _ in 0..header.len / 4 {
268-
ips.push(Ipv4Addr::read::<E>(buf)?);
269-
}
270-
258+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
271259
Self::DomainNameServer(ips)
272260
}
273-
OptionTag::LogServer => todo!(),
274-
OptionTag::CookieServer => todo!(),
275-
OptionTag::LprServer => todo!(),
276-
OptionTag::ImpressServer => todo!(),
261+
OptionTag::LogServer => {
262+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
263+
Self::LogServer(ips)
264+
}
265+
OptionTag::CookieServer => {
266+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
267+
Self::CookieServer(ips)
268+
}
269+
OptionTag::LprServer => {
270+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
271+
Self::LprServer(ips)
272+
}
273+
OptionTag::ImpressServer => {
274+
let ips = read_ip_addrs_set::<E>(buf, header.len)?;
275+
Self::ImpressServer(ips)
276+
}
277277
OptionTag::ResourceLocationServer => todo!(),
278278
OptionTag::HostName => {
279279
let b = buf.read_vec(header.len as usize)?;
@@ -349,23 +349,23 @@ impl OptionData {
349349
Ok(option_data)
350350
}
351351

352-
pub fn len(&self) -> u8 {
352+
pub fn size(&self) -> u8 {
353353
match self {
354354
OptionData::Pad => 1,
355355
OptionData::End => 1,
356356
OptionData::SubnetMask(_) => 4,
357-
OptionData::TimeOffset => 4,
358-
OptionData::Router(ips) => ips.len() as u8,
359-
OptionData::TimeServer => todo!(),
360-
OptionData::NameServer => todo!(),
361-
OptionData::DomainNameServer(ips) => ips.len() as u8,
362-
OptionData::LogServer => todo!(),
363-
OptionData::CookieServer => todo!(),
364-
OptionData::LprServer => todo!(),
365-
OptionData::ImpressServer => todo!(),
366-
OptionData::ResourceLocationServer => todo!(),
357+
OptionData::TimeOffset(_) => 4,
358+
OptionData::Router(ips) => (ips.len() * 4) as u8,
359+
OptionData::TimeServer(ips) => (ips.len() * 4) as u8,
360+
OptionData::NameServer(ips) => (ips.len() * 4) as u8,
361+
OptionData::DomainNameServer(ips) => (ips.len() * 4) as u8,
362+
OptionData::LogServer(ips) => (ips.len() * 4) as u8,
363+
OptionData::CookieServer(ips) => (ips.len() * 4) as u8,
364+
OptionData::LprServer(ips) => (ips.len() * 4) as u8,
365+
OptionData::ImpressServer(ips) => (ips.len() * 4) as u8,
366+
OptionData::ResourceLocationServer(ips) => (ips.len() * 4) as u8,
367367
OptionData::HostName(h) => h.len() as u8,
368-
OptionData::BootFileSize => 2,
368+
OptionData::BootFileSize(_) => 2,
369369
OptionData::MeritDumpFile => todo!(),
370370
OptionData::DomainName => todo!(),
371371
OptionData::SwapServer => todo!(),
@@ -417,3 +417,22 @@ impl OptionData {
417417
}
418418
}
419419
}
420+
421+
/// Reads a set of IPv4 addresses. This function ensures that the provided
422+
/// length is at least 4 and a multiple of 4.
423+
fn read_ip_addrs_set<E: Endianness>(
424+
buf: &mut ReadBuffer,
425+
len: u8,
426+
) -> Result<Vec<Ipv4Addr>, OptionDataError> {
427+
if len < 4 || len % 4 != 0 {
428+
return Err(OptionDataError::InvalidData);
429+
}
430+
431+
let mut ips = Vec::new();
432+
433+
for _ in 0..len / 4 {
434+
ips.push(Ipv4Addr::read::<E>(buf)?);
435+
}
436+
437+
Ok(ips)
438+
}

crates/lib-dhcp/src/types/option/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl Writeable for DhcpOption {
5252
impl DhcpOption {
5353
pub fn new(tag: OptionTag, data: OptionData) -> Self {
5454
let header = OptionHeader {
55-
len: data.len(),
55+
len: data.size(),
5656
tag,
5757
};
5858

0 commit comments

Comments
 (0)