1use bstr::BString;
6use log::debug;
7use net_types::ethernet::Mac as MacAddr;
8use net_types::ip::{Ipv4, NotSubnetMaskError, PrefixLength};
9use num_derive::FromPrimitive;
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use std::net::Ipv4Addr;
13use std::num::{NonZeroU8, NonZeroU16};
14use thiserror::Error;
15
16mod size_constrained;
17pub use crate::size_constrained::{
18 AtLeast, AtMostBytes, Error as SizeConstrainedError, U8_MAX_AS_USIZE,
19};
20
21mod size_of_contents;
22use crate::size_of_contents::SizeOfContents as _;
23
24pub const SERVER_PORT: NonZeroU16 = NonZeroU16::new(67).unwrap();
31
32pub const CLIENT_PORT: NonZeroU16 = NonZeroU16::new(68).unwrap();
39
40const OP_IDX: usize = 0;
41const XID_IDX: usize = 4;
46const SECS_IDX: usize = 8;
47const FLAGS_IDX: usize = 10;
48const CIADDR_IDX: usize = 12;
49const YIADDR_IDX: usize = 16;
50const SIADDR_IDX: usize = 20;
51const GIADDR_IDX: usize = 24;
52const CHADDR_IDX: usize = 28;
53const SNAME_IDX: usize = 44;
54const FILE_IDX: usize = 108;
55const OPTIONS_START_IDX: usize = 236;
56
57const ETHERNET_HTYPE: u8 = 1;
58const ETHERNET_HLEN: u8 = 6;
59const HOPS_DEFAULT: u8 = 0;
60const MAGIC_COOKIE: [u8; 4] = [99, 130, 83, 99];
61
62const UNUSED_CHADDR_BYTES: usize = 10;
63
64const CHADDR_LEN: usize = 6;
65const SNAME_LEN: usize = 64;
66const FILE_LEN: usize = 128;
67const IPV4_ADDR_LEN: usize = 4;
68
69const MIN_MESSAGE_SIZE: u16 = 576;
73
74const MIN_MTU_VAL: u16 = 68;
77
78const ASCII_NULL: char = '\x00';
79
80#[derive(Debug, Error, PartialEq)]
81pub enum ProtocolError {
82 #[error("invalid buffer length: {}", _0)]
83 InvalidBufferLength(usize),
84
85 #[cfg(target_os = "fuchsia")]
86 #[error("option not supported in fuchsia.net.dhcp: {:?}", _0)]
87 InvalidFidlOption(DhcpOption),
88 #[error("invalid message type: {}", _0)]
89 InvalidMessageType(u8),
90 #[error("invalid bootp op code: {}", _0)]
91 InvalidOpCode(u8),
92 #[error("invalid option code: {}", _0)]
93 InvalidOptionCode(u8),
94 #[error("invalid option value. code: {}, value: {:?}", _0, _1)]
95 InvalidOptionValue(OptionCode, Vec<u8>),
96 #[error("missing opcode")]
97 MissingOpCode,
98 #[error("missing expected option: {}", _0)]
99 MissingOption(OptionCode),
100 #[error(
101 "malformed option {code} needs at least {want} bytes, but buffer has {remaining} remaining"
102 )]
103 MalformedOption { code: u8, remaining: usize, want: usize },
104
105 #[cfg(target_os = "fuchsia")]
106 #[error("received unknown fidl option variant")]
107 UnknownFidlOption,
108 #[error("invalid utf-8 after buffer index: {}", _0)]
109 Utf8(usize),
110 #[error("invalid protocol field {} = {} for message type {}", field, value, msg_type)]
111 InvalidField { field: String, value: String, msg_type: MessageType },
112}
113
114#[derive(Debug, Error, PartialEq)]
115#[error("Buffer is of invalid length: {0}")]
116struct InvalidBufferLengthError(usize);
117
118impl From<InvalidBufferLengthError> for ProtocolError {
119 fn from(err: InvalidBufferLengthError) -> ProtocolError {
120 let InvalidBufferLengthError(len) = err;
121 ProtocolError::InvalidBufferLength(len)
122 }
123}
124
125impl From<InvalidBufferLengthError> for BooleanConversionError {
126 fn from(err: InvalidBufferLengthError) -> BooleanConversionError {
127 let InvalidBufferLengthError(len) = err;
128 BooleanConversionError::InvalidBufferLength(len)
129 }
130}
131
132#[derive(Debug, PartialEq)]
138pub struct Message {
139 pub op: OpCode,
140 pub xid: u32,
141 pub secs: u16,
142 pub bdcast_flag: bool,
143 pub ciaddr: Ipv4Addr,
145 pub yiaddr: Ipv4Addr,
147 pub siaddr: Ipv4Addr,
149 pub giaddr: Ipv4Addr,
151 pub chaddr: MacAddr,
154 pub sname: BString,
156 pub file: BString,
158 pub options: Vec<DhcpOption>,
159}
160
161impl Message {
162 pub fn from_buffer(buf: &[u8]) -> Result<Self, ProtocolError> {
167 let options =
168 buf.get(OPTIONS_START_IDX..).ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
169 let options = {
170 let magic_cookie = options
171 .get(..MAGIC_COOKIE.len())
172 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
173 let options = options
174 .get(MAGIC_COOKIE.len()..)
175 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
176 if magic_cookie == MAGIC_COOKIE {
177 parse_options(options, Vec::new())?
178 } else {
179 Vec::new()
180 }
181 };
182
183 let overload = options.iter().find_map(|v| match v {
206 &DhcpOption::OptionOverload(overload) => Some(overload),
207 _ => None,
208 });
209 let sname =
210 buf.get(SNAME_IDX..FILE_IDX).ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
211 let file = buf
212 .get(FILE_IDX..OPTIONS_START_IDX)
213 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
214 let options = match overload {
215 Some(overload) => {
216 let extra_opts = match overload {
217 Overload::SName => sname,
218 Overload::File => file,
219 Overload::Both => buf
220 .get(SNAME_IDX..OPTIONS_START_IDX)
221 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?,
222 };
223 parse_options(extra_opts, options)?
224 }
225 None => options,
226 };
227 Ok(Self {
228 op: OpCode::try_from(*buf.get(OP_IDX).ok_or(ProtocolError::MissingOpCode)?)?,
229 xid: u32::from_be_bytes(
230 <[u8; 4]>::try_from(
231 buf.get(XID_IDX..SECS_IDX)
232 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?,
233 )
234 .map_err(|std::array::TryFromSliceError { .. }| {
235 ProtocolError::InvalidBufferLength(buf.len())
236 })?,
237 ),
238 secs: u16::from_be_bytes(
239 <[u8; 2]>::try_from(
240 buf.get(SECS_IDX..FLAGS_IDX)
241 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?,
242 )
243 .map_err(|std::array::TryFromSliceError { .. }| {
244 ProtocolError::InvalidBufferLength(buf.len())
245 })?,
246 ),
247 bdcast_flag: *buf
248 .get(FLAGS_IDX)
249 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?
250 != 0,
251 ciaddr: ip_addr_from_buf_at(buf, CIADDR_IDX)?,
252 yiaddr: ip_addr_from_buf_at(buf, YIADDR_IDX)?,
253 siaddr: ip_addr_from_buf_at(buf, SIADDR_IDX)?,
254 giaddr: ip_addr_from_buf_at(buf, GIADDR_IDX)?,
255 chaddr: MacAddr::new(
256 buf.get(CHADDR_IDX..CHADDR_IDX + CHADDR_LEN)
257 .ok_or(ProtocolError::InvalidBufferLength(buf.len()))?
258 .try_into()
259 .map_err(|std::array::TryFromSliceError { .. }| {
260 ProtocolError::InvalidBufferLength(buf.len())
261 })?,
262 ),
263 sname: match overload {
264 Some(Overload::SName) | Some(Overload::Both) => BString::default(),
265 Some(Overload::File) | None => buf_to_msg_string(sname),
266 },
267 file: match overload {
268 Some(Overload::File) | Some(Overload::Both) => BString::default(),
269 Some(Overload::SName) | None => buf_to_msg_string(file),
270 },
271 options,
272 })
273 }
274
275 pub fn serialize(self) -> Vec<u8> {
277 let Self {
278 op,
279 xid,
280 secs,
281 bdcast_flag,
282 ciaddr,
283 yiaddr,
284 siaddr,
285 giaddr,
286 chaddr,
287 sname,
288 file,
289 options,
290 } = self;
291 let mut buffer = Vec::with_capacity(OPTIONS_START_IDX);
292 buffer.push(op.into());
293 buffer.push(ETHERNET_HTYPE);
294 buffer.push(ETHERNET_HLEN);
295 buffer.push(HOPS_DEFAULT);
296 buffer.extend_from_slice(&xid.to_be_bytes());
297 buffer.extend_from_slice(&secs.to_be_bytes());
298 if bdcast_flag {
299 buffer.push(128u8);
301 } else {
302 buffer.push(0u8);
303 }
304 buffer.push(0u8);
305 buffer.extend_from_slice(&ciaddr.octets());
306 buffer.extend_from_slice(&yiaddr.octets());
307 buffer.extend_from_slice(&siaddr.octets());
308 buffer.extend_from_slice(&giaddr.octets());
309 buffer.extend_from_slice(&chaddr.bytes().as_ref());
310 buffer.extend_from_slice(&[0u8; UNUSED_CHADDR_BYTES]);
311 trunc_string_to_n_and_push(&sname, SNAME_LEN, &mut buffer);
312 trunc_string_to_n_and_push(&file, FILE_LEN, &mut buffer);
313
314 buffer.extend_from_slice(&MAGIC_COOKIE);
315 for option in options.into_iter() {
316 option.serialize_to(&mut buffer);
317 }
318 buffer.push(OptionCode::End.into());
319
320 buffer
321 }
322
323 pub fn get_dhcp_type(&self) -> Result<MessageType, ProtocolError> {
325 self.options
326 .iter()
327 .filter_map(|opt| match opt {
328 DhcpOption::DhcpMessageType(v) => Some(*v),
329 _ => None,
330 })
331 .next()
332 .ok_or(ProtocolError::MissingOption(OptionCode::DhcpMessageType))
333 }
334}
335
336pub mod identifier {
337 use super::{CHADDR_LEN, DhcpOption, Message};
338 use net_types::ethernet::Mac as MacAddr;
339 use std::convert::TryInto as _;
340
341 const CLIENT_IDENTIFIER_ID: &'static str = "id";
342 const CLIENT_IDENTIFIER_CHADDR: &'static str = "chaddr";
343
344 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
346 pub struct ClientIdentifier {
347 inner: ClientIdentifierInner,
348 }
349
350 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
351 enum ClientIdentifierInner {
352 Id(Vec<u8>),
355 Chaddr(MacAddr),
358 }
359
360 impl From<MacAddr> for ClientIdentifier {
361 fn from(v: MacAddr) -> Self {
362 Self { inner: ClientIdentifierInner::Chaddr(v) }
363 }
364 }
365
366 impl From<&Message> for ClientIdentifier {
367 fn from(msg: &Message) -> ClientIdentifier {
373 msg.options
374 .iter()
375 .find_map(|opt| match opt {
376 DhcpOption::ClientIdentifier(v) => Some(ClientIdentifier {
377 inner: ClientIdentifierInner::Id(v.clone().into()),
378 }),
379 _ => None,
380 })
381 .unwrap_or_else(|| ClientIdentifier::from(msg.chaddr))
382 }
383 }
384
385 impl std::str::FromStr for ClientIdentifier {
386 type Err = anyhow::Error;
387
388 fn from_str(s: &str) -> Result<Self, Self::Err> {
389 let mut id_parts = s.splitn(2, ":");
390 let id_type = id_parts
391 .next()
392 .ok_or_else(|| anyhow::anyhow!("no client id type found in string: {}", s))?;
393 let id = id_parts
394 .next()
395 .ok_or_else(|| anyhow::anyhow!("no client id found in string: {}", s))?;
396 match id_parts.next() {
397 None => (),
398 Some(v) => {
399 return Err(anyhow::anyhow!(
400 "client id string contained unexpected fields: {}",
401 v
402 ));
403 }
404 };
405 let id = hex::decode(id)?;
406 match id_type {
407 CLIENT_IDENTIFIER_ID => Ok(Self { inner: ClientIdentifierInner::Id(id) }),
408 CLIENT_IDENTIFIER_CHADDR => Ok(Self {
409 inner: ClientIdentifierInner::Chaddr(MacAddr::new(
410 id.get(..CHADDR_LEN)
411 .ok_or_else(|| {
412 anyhow::anyhow!("client id had insufficient length: {:?}", id)
413 })?
414 .try_into()?,
415 )),
416 }),
417 id_type => Err(anyhow::anyhow!("unrecognized client id type: {}", id_type)),
418 }
419 }
420 }
421
422 impl std::fmt::Display for ClientIdentifier {
423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424 use zerocopy::IntoBytes as _;
425 let (id_type, id) = match self {
426 Self { inner: ClientIdentifierInner::Id(v) } => (CLIENT_IDENTIFIER_ID, &v[..]),
427 Self { inner: ClientIdentifierInner::Chaddr(v) } => {
428 (CLIENT_IDENTIFIER_CHADDR, v.as_bytes())
429 }
430 };
431 write!(f, "{}:{}", id_type, hex::encode(id))
432 }
433 }
434}
435
436#[derive(FromPrimitive, Copy, Clone, Debug, PartialEq)]
446#[repr(u8)]
447pub enum OpCode {
448 BOOTREQUEST = 1,
449 BOOTREPLY = 2,
450}
451
452impl fmt::Display for OpCode {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 match self {
455 OpCode::BOOTREQUEST => write!(f, "BOOTREQUEST"),
456 OpCode::BOOTREPLY => write!(f, "BOOTREPLY"),
457 }
458 }
459}
460
461impl From<OpCode> for u8 {
462 fn from(code: OpCode) -> u8 {
463 code as u8
464 }
465}
466
467impl TryFrom<u8> for OpCode {
468 type Error = ProtocolError;
469
470 fn try_from(n: u8) -> Result<Self, Self::Error> {
471 <Self as num_traits::FromPrimitive>::from_u8(n).ok_or(ProtocolError::InvalidOpCode(n))
472 }
473}
474
475#[derive(
482 Copy, Clone, Debug, Deserialize, Eq, FromPrimitive, Hash, PartialEq, Serialize, PartialOrd, Ord,
483)]
484#[repr(u8)]
485pub enum OptionCode {
486 Pad = 0,
487 SubnetMask = 1,
488 TimeOffset = 2,
489 Router = 3,
490 TimeServer = 4,
491 NameServer = 5,
492 DomainNameServer = 6,
493 LogServer = 7,
494 CookieServer = 8,
495 LprServer = 9,
496 ImpressServer = 10,
497 ResourceLocationServer = 11,
498 HostName = 12,
499 BootFileSize = 13,
500 MeritDumpFile = 14,
501 DomainName = 15,
502 SwapServer = 16,
503 RootPath = 17,
504 ExtensionsPath = 18,
505 IpForwarding = 19,
506 NonLocalSourceRouting = 20,
507 PolicyFilter = 21,
508 MaxDatagramReassemblySize = 22,
509 DefaultIpTtl = 23,
510 PathMtuAgingTimeout = 24,
511 PathMtuPlateauTable = 25,
512 InterfaceMtu = 26,
513 AllSubnetsLocal = 27,
514 BroadcastAddress = 28,
515 PerformMaskDiscovery = 29,
516 MaskSupplier = 30,
517 PerformRouterDiscovery = 31,
518 RouterSolicitationAddress = 32,
519 StaticRoute = 33,
520 TrailerEncapsulation = 34,
521 ArpCacheTimeout = 35,
522 EthernetEncapsulation = 36,
523 TcpDefaultTtl = 37,
524 TcpKeepaliveInterval = 38,
525 TcpKeepaliveGarbage = 39,
526 NetworkInformationServiceDomain = 40,
527 NetworkInformationServers = 41,
528 NetworkTimeProtocolServers = 42,
529 VendorSpecificInformation = 43,
530 NetBiosOverTcpipNameServer = 44,
531 NetBiosOverTcpipDatagramDistributionServer = 45,
532 NetBiosOverTcpipNodeType = 46,
533 NetBiosOverTcpipScope = 47,
534 XWindowSystemFontServer = 48,
535 XWindowSystemDisplayManager = 49,
536 RequestedIpAddress = 50,
537 IpAddressLeaseTime = 51,
538 OptionOverload = 52,
539 DhcpMessageType = 53,
540 ServerIdentifier = 54,
541 ParameterRequestList = 55,
542 Message = 56,
543 MaxDhcpMessageSize = 57,
544 RenewalTimeValue = 58,
545 RebindingTimeValue = 59,
546 VendorClassIdentifier = 60,
547 ClientIdentifier = 61,
548 NetworkInformationServicePlusDomain = 64,
549 NetworkInformationServicePlusServers = 65,
550 TftpServerName = 66,
551 BootfileName = 67,
552 MobileIpHomeAgent = 68,
553 SmtpServer = 69,
554 Pop3Server = 70,
555 NntpServer = 71,
556 DefaultWwwServer = 72,
557 DefaultFingerServer = 73,
558 DefaultIrcServer = 74,
559 StreetTalkServer = 75,
560 StreetTalkDirectoryAssistanceServer = 76,
561 End = 255,
562}
563
564impl From<OptionCode> for u8 {
565 fn from(code: OptionCode) -> u8 {
566 code as u8
567 }
568}
569
570impl TryFrom<u8> for OptionCode {
571 type Error = ProtocolError;
572
573 fn try_from(n: u8) -> Result<Self, Self::Error> {
574 <Self as num_traits::FromPrimitive>::from_u8(n).ok_or(ProtocolError::InvalidOptionCode(n))
575 }
576}
577
578impl fmt::Display for OptionCode {
579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580 fmt::Debug::fmt(&self, f)
581 }
582}
583
584mod prefix_length {
585 use std::net::Ipv4Addr;
586
587 use net_types::ip::{Ipv4, NotSubnetMaskError, PrefixLength};
588 use serde::Serialize as _;
589 use serde::de::{Deserialize as _, Error};
590
591 pub(super) fn serialize<S: serde::Serializer>(
592 prefix_length: &PrefixLength<Ipv4>,
593 serializer: S,
594 ) -> Result<S::Ok, S::Error> {
595 Ipv4Addr::serialize(&Ipv4Addr::from(prefix_length.get_mask()), serializer)
596 }
597
598 pub(super) fn deserialize<'de, D: serde::Deserializer<'de>>(
599 deserializer: D,
600 ) -> Result<PrefixLength<Ipv4>, D::Error> {
601 let addr = Ipv4Addr::deserialize(deserializer)?;
602 PrefixLength::try_from_subnet_mask(addr.into())
603 .map_err(|NotSubnetMaskError| D::Error::custom("not a valid subnet mask"))
604 }
605}
606
607#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
614pub enum DhcpOption {
615 Pad(),
616 End(),
617 #[serde(with = "prefix_length")]
618 SubnetMask(PrefixLength<Ipv4>),
619 TimeOffset(i32),
620 Router(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
623 TimeServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
626 NameServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
629 DomainNameServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
632 LogServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
635 CookieServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
638 LprServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
641 ImpressServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
644 ResourceLocationServer(
647 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
648 ),
649 HostName(String),
650 BootFileSize(u16),
651 MeritDumpFile(String),
652 DomainName(String),
653 SwapServer(Ipv4Addr),
654 RootPath(String),
655 ExtensionsPath(String),
656 IpForwarding(bool),
657 NonLocalSourceRouting(bool),
658 PolicyFilter(AtLeast<2, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
661 MaxDatagramReassemblySize(u16),
662 DefaultIpTtl(NonZeroU8),
664 PathMtuAgingTimeout(u32),
665 PathMtuPlateauTable(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<u16>>>),
668 InterfaceMtu(u16),
669 AllSubnetsLocal(bool),
670 BroadcastAddress(Ipv4Addr),
671 PerformMaskDiscovery(bool),
672 MaskSupplier(bool),
673 PerformRouterDiscovery(bool),
674 RouterSolicitationAddress(Ipv4Addr),
675 StaticRoute(AtLeast<2, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
678 TrailerEncapsulation(bool),
679 ArpCacheTimeout(u32),
680 EthernetEncapsulation(bool),
681 TcpDefaultTtl(NonZeroU8),
683 TcpKeepaliveInterval(u32),
684 TcpKeepaliveGarbage(bool),
685 NetworkInformationServiceDomain(String),
686 NetworkInformationServers(
689 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
690 ),
691 NetworkTimeProtocolServers(
694 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
695 ),
696 VendorSpecificInformation(
699 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<u8>>>,
700 ),
701 NetBiosOverTcpipNameServer(
704 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
705 ),
706 NetBiosOverTcpipDatagramDistributionServer(
709 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
710 ),
711 NetBiosOverTcpipNodeType(NodeType),
712 NetBiosOverTcpipScope(String),
713 XWindowSystemFontServer(
716 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
717 ),
718 XWindowSystemDisplayManager(
721 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
722 ),
723 NetworkInformationServicePlusDomain(String),
724 NetworkInformationServicePlusServers(
727 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
728 ),
729 MobileIpHomeAgent(
732 AtLeast<0, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
733 ),
734 SmtpServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
737 Pop3Server(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
740 NntpServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
743 DefaultWwwServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
746 DefaultFingerServer(
749 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
750 ),
751 DefaultIrcServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
754 StreetTalkServer(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>),
757 StreetTalkDirectoryAssistanceServer(
760 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
761 ),
762 RequestedIpAddress(Ipv4Addr),
763 IpAddressLeaseTime(u32),
764 OptionOverload(Overload),
765 TftpServerName(String),
766 BootfileName(String),
767 DhcpMessageType(MessageType),
768 ServerIdentifier(Ipv4Addr),
769 ParameterRequestList(
772 AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<OptionCode>>>,
773 ),
774 Message(String),
798 MaxDhcpMessageSize(u16),
799 RenewalTimeValue(u32),
800 RebindingTimeValue(u32),
801 VendorClassIdentifier(AtLeast<1, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<u8>>>),
804 ClientIdentifier(
807 AtLeast<
808 { CLIENT_IDENTIFIER_MINIMUM_LENGTH },
809 AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<u8>>,
810 >,
811 ),
812}
813
814pub const CLIENT_IDENTIFIER_MINIMUM_LENGTH: usize = 2;
816
817macro_rules! option_to_code {
820 ($option:ident, $(DhcpOption::$variant:tt($($v:tt)*)),*) => {
821 match $option {
822 $(DhcpOption::$variant($($v)*) => OptionCode::$variant,)*
823 }
824 };
825}
826
827impl DhcpOption {
828 fn from_raw_parts(code: OptionCode, val: &[u8]) -> Result<Self, ProtocolError> {
829 match code {
830 OptionCode::Pad => Ok(DhcpOption::Pad()),
831 OptionCode::End => Ok(DhcpOption::End()),
832 OptionCode::SubnetMask => {
833 let addr = bytes_to_addr(val)?;
834 Ok(DhcpOption::SubnetMask(
835 PrefixLength::try_from_subnet_mask(addr.into()).map_err(
836 |NotSubnetMaskError| ProtocolError::InvalidOptionValue(code, val.to_vec()),
837 )?,
838 ))
839 }
840 OptionCode::TimeOffset => {
841 let offset = get_byte_array::<4>(val).map(i32::from_be_bytes)?;
842 Ok(DhcpOption::TimeOffset(offset))
843 }
844 OptionCode::Router => Ok(DhcpOption::Router(bytes_to_addrs(val)?)),
845 OptionCode::TimeServer => Ok(DhcpOption::TimeServer(bytes_to_addrs(val)?)),
846 OptionCode::NameServer => Ok(DhcpOption::NameServer(bytes_to_addrs(val)?)),
847 OptionCode::DomainNameServer => Ok(DhcpOption::DomainNameServer(bytes_to_addrs(val)?)),
848 OptionCode::LogServer => Ok(DhcpOption::LogServer(bytes_to_addrs(val)?)),
849 OptionCode::CookieServer => Ok(DhcpOption::CookieServer(bytes_to_addrs(val)?)),
850 OptionCode::LprServer => Ok(DhcpOption::LprServer(bytes_to_addrs(val)?)),
851 OptionCode::ImpressServer => Ok(DhcpOption::ImpressServer(bytes_to_addrs(val)?)),
852 OptionCode::ResourceLocationServer => {
853 Ok(DhcpOption::ResourceLocationServer(bytes_to_addrs(val)?))
854 }
855 OptionCode::HostName => Ok(DhcpOption::HostName(bytes_to_nonempty_str(val)?)),
856 OptionCode::BootFileSize => {
857 let size = get_byte_array::<2>(val).map(u16::from_be_bytes)?;
858 Ok(DhcpOption::BootFileSize(size))
859 }
860 OptionCode::MeritDumpFile => Ok(DhcpOption::MeritDumpFile(bytes_to_nonempty_str(val)?)),
861 OptionCode::DomainName => Ok(DhcpOption::DomainName(bytes_to_nonempty_str(val)?)),
862 OptionCode::SwapServer => Ok(DhcpOption::SwapServer(bytes_to_addr(val)?)),
863 OptionCode::RootPath => Ok(DhcpOption::RootPath(bytes_to_nonempty_str(val)?)),
864 OptionCode::ExtensionsPath => {
865 Ok(DhcpOption::ExtensionsPath(bytes_to_nonempty_str(val)?))
866 }
867 OptionCode::IpForwarding => {
868 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
869 Ok(DhcpOption::IpForwarding(flag))
870 }
871 OptionCode::NonLocalSourceRouting => {
872 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
873 Ok(DhcpOption::NonLocalSourceRouting(flag))
874 }
875 OptionCode::PolicyFilter => {
876 let addrs = bytes_to_addrs(val)?;
877 if addrs.len() < 2 || addrs.len() % 2 != 0 {
878 return Err(ProtocolError::InvalidBufferLength(val.len()));
879 }
880 Ok(DhcpOption::PolicyFilter(addrs))
881 }
882 OptionCode::MaxDatagramReassemblySize => {
883 let max_datagram = get_byte_array::<2>(val).map(u16::from_be_bytes)?;
884 if max_datagram < MIN_MESSAGE_SIZE {
885 return Err(ProtocolError::InvalidOptionValue(code, val.to_vec()));
886 }
887 Ok(DhcpOption::MaxDatagramReassemblySize(max_datagram))
888 }
889 OptionCode::DefaultIpTtl => {
890 let ttl = get_byte(val)?;
891 let ttl = NonZeroU8::new(ttl)
892 .ok_or_else(|| ProtocolError::InvalidOptionValue(code, val.to_vec()))?;
893 Ok(DhcpOption::DefaultIpTtl(ttl))
894 }
895 OptionCode::PathMtuAgingTimeout => {
896 let timeout = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
897 Ok(DhcpOption::PathMtuAgingTimeout(timeout))
898 }
899 OptionCode::PathMtuPlateauTable => {
900 let mtus = val
901 .chunks(2)
902 .map(|chunk| get_byte_array::<2>(chunk).map(u16::from_be_bytes))
903 .collect::<Result<Vec<u16>, InvalidBufferLengthError>>()
904 .map_err(|InvalidBufferLengthError(_)| {
905 ProtocolError::InvalidBufferLength(val.len())
906 })?;
907 Ok(DhcpOption::PathMtuPlateauTable(mtus.try_into().map_err(
908 |(size_constrained::Error::SizeConstraintViolated, _)| {
909 ProtocolError::InvalidBufferLength(val.len())
910 },
911 )?))
912 }
913 OptionCode::InterfaceMtu => {
914 let mtu = get_byte_array::<2>(val).map(u16::from_be_bytes)?;
915 if mtu < MIN_MTU_VAL {
916 return Err(ProtocolError::InvalidOptionValue(code, val.to_vec()));
917 }
918 Ok(DhcpOption::InterfaceMtu(mtu))
919 }
920 OptionCode::AllSubnetsLocal => {
921 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
922 Ok(DhcpOption::AllSubnetsLocal(flag))
923 }
924 OptionCode::BroadcastAddress => Ok(DhcpOption::BroadcastAddress(bytes_to_addr(val)?)),
925 OptionCode::PerformMaskDiscovery => {
926 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
927 Ok(DhcpOption::PerformMaskDiscovery(flag))
928 }
929 OptionCode::MaskSupplier => {
930 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
931 Ok(DhcpOption::MaskSupplier(flag))
932 }
933 OptionCode::PerformRouterDiscovery => {
934 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
935 Ok(DhcpOption::PerformRouterDiscovery(flag))
936 }
937 OptionCode::RouterSolicitationAddress => {
938 Ok(DhcpOption::RouterSolicitationAddress(bytes_to_addr(val)?))
939 }
940 OptionCode::StaticRoute => {
941 let addrs = bytes_to_addrs(val)?;
942 if addrs.len() < 2 || addrs.len() % 2 != 0 {
943 return Err(ProtocolError::InvalidBufferLength(val.len()));
944 }
945 Ok(DhcpOption::StaticRoute(addrs))
946 }
947 OptionCode::TrailerEncapsulation => {
948 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
949 Ok(DhcpOption::TrailerEncapsulation(flag))
950 }
951 OptionCode::ArpCacheTimeout => {
952 let timeout = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
953 Ok(DhcpOption::ArpCacheTimeout(timeout))
954 }
955 OptionCode::EthernetEncapsulation => {
956 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
957 Ok(DhcpOption::EthernetEncapsulation(flag))
958 }
959 OptionCode::TcpDefaultTtl => {
960 let ttl = get_byte(val)?;
961 let ttl = NonZeroU8::new(ttl)
962 .ok_or_else(|| ProtocolError::InvalidOptionValue(code, val.to_vec()))?;
963 Ok(DhcpOption::TcpDefaultTtl(ttl))
964 }
965 OptionCode::TcpKeepaliveInterval => {
966 let interval = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
967 Ok(DhcpOption::TcpKeepaliveInterval(interval))
968 }
969 OptionCode::TcpKeepaliveGarbage => {
970 let flag = bytes_to_bool(val).map_err(|e| e.to_protocol(code))?;
971 Ok(DhcpOption::TcpKeepaliveGarbage(flag))
972 }
973 OptionCode::NetworkInformationServiceDomain => {
974 let name = bytes_to_nonempty_str(val)?;
975 Ok(DhcpOption::NetworkInformationServiceDomain(name))
976 }
977 OptionCode::NetworkInformationServers => {
978 Ok(DhcpOption::NetworkInformationServers(bytes_to_addrs(val)?))
979 }
980 OptionCode::NetworkTimeProtocolServers => {
981 Ok(DhcpOption::NetworkTimeProtocolServers(bytes_to_addrs(val)?))
982 }
983 OptionCode::VendorSpecificInformation => {
984 Ok(DhcpOption::VendorSpecificInformation(val.to_owned().try_into().map_err(
985 |(size_constrained::Error::SizeConstraintViolated, _)| {
986 ProtocolError::InvalidBufferLength(val.len())
987 },
988 )?))
989 }
990 OptionCode::NetBiosOverTcpipNameServer => {
991 Ok(DhcpOption::NetBiosOverTcpipNameServer(bytes_to_addrs(val)?))
992 }
993 OptionCode::NetBiosOverTcpipDatagramDistributionServer => {
994 Ok(DhcpOption::NetBiosOverTcpipDatagramDistributionServer(bytes_to_addrs(val)?))
995 }
996 OptionCode::NetBiosOverTcpipNodeType => {
997 let byte = get_byte(val)?;
998 Ok(DhcpOption::NetBiosOverTcpipNodeType(NodeType::try_from(byte)?))
999 }
1000 OptionCode::NetBiosOverTcpipScope => {
1001 Ok(DhcpOption::NetBiosOverTcpipScope(bytes_to_nonempty_str(val)?))
1002 }
1003 OptionCode::XWindowSystemFontServer => {
1004 Ok(DhcpOption::XWindowSystemFontServer(bytes_to_addrs(val)?))
1005 }
1006 OptionCode::XWindowSystemDisplayManager => {
1007 Ok(DhcpOption::XWindowSystemDisplayManager(bytes_to_addrs(val)?))
1008 }
1009 OptionCode::NetworkInformationServicePlusDomain => {
1010 Ok(DhcpOption::NetworkInformationServicePlusDomain(bytes_to_nonempty_str(val)?))
1011 }
1012 OptionCode::NetworkInformationServicePlusServers => {
1013 Ok(DhcpOption::NetworkInformationServicePlusServers(bytes_to_addrs(val)?))
1014 }
1015 OptionCode::MobileIpHomeAgent => {
1016 Ok(DhcpOption::MobileIpHomeAgent(bytes_to_addrs(val)?))
1017 }
1018 OptionCode::SmtpServer => Ok(DhcpOption::SmtpServer(bytes_to_addrs(val)?)),
1019 OptionCode::Pop3Server => Ok(DhcpOption::Pop3Server(bytes_to_addrs(val)?)),
1020 OptionCode::NntpServer => Ok(DhcpOption::NntpServer(bytes_to_addrs(val)?)),
1021 OptionCode::DefaultWwwServer => Ok(DhcpOption::DefaultWwwServer(bytes_to_addrs(val)?)),
1022 OptionCode::DefaultFingerServer => {
1023 Ok(DhcpOption::DefaultFingerServer(bytes_to_addrs(val)?))
1024 }
1025 OptionCode::DefaultIrcServer => Ok(DhcpOption::DefaultIrcServer(bytes_to_addrs(val)?)),
1026 OptionCode::StreetTalkServer => Ok(DhcpOption::StreetTalkServer(bytes_to_addrs(val)?)),
1027 OptionCode::StreetTalkDirectoryAssistanceServer => {
1028 Ok(DhcpOption::StreetTalkDirectoryAssistanceServer(bytes_to_addrs(val)?))
1029 }
1030 OptionCode::RequestedIpAddress => {
1031 Ok(DhcpOption::RequestedIpAddress(bytes_to_addr(val)?))
1032 }
1033 OptionCode::IpAddressLeaseTime => {
1034 let lease_time = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
1035 Ok(DhcpOption::IpAddressLeaseTime(lease_time))
1036 }
1037 OptionCode::OptionOverload => {
1038 let overload = Overload::try_from(
1039 *val.first().ok_or(ProtocolError::InvalidBufferLength(val.len()))?,
1040 )?;
1041 Ok(DhcpOption::OptionOverload(overload))
1042 }
1043 OptionCode::TftpServerName => {
1044 let name = bytes_to_nonempty_str(val)?;
1045 Ok(DhcpOption::TftpServerName(name))
1046 }
1047 OptionCode::BootfileName => {
1048 let name = bytes_to_nonempty_str(val)?;
1049 Ok(DhcpOption::BootfileName(name))
1050 }
1051 OptionCode::DhcpMessageType => {
1052 let message_type = MessageType::try_from(
1053 *val.first().ok_or(ProtocolError::InvalidBufferLength(val.len()))?,
1054 )?;
1055 Ok(DhcpOption::DhcpMessageType(message_type))
1056 }
1057 OptionCode::ServerIdentifier => Ok(DhcpOption::ServerIdentifier(bytes_to_addr(val)?)),
1058 OptionCode::ParameterRequestList => {
1059 let opcodes = val
1060 .iter()
1061 .filter_map(|code| OptionCode::try_from(*code).ok())
1062 .collect::<Vec<_>>();
1063 Ok(DhcpOption::ParameterRequestList(opcodes.try_into().map_err(
1071 |(size_constrained::Error::SizeConstraintViolated, _)| {
1072 ProtocolError::InvalidBufferLength(val.len())
1073 },
1074 )?))
1075 }
1076 OptionCode::Message => Ok(DhcpOption::Message(bytes_to_nonempty_str(val)?)),
1077 OptionCode::MaxDhcpMessageSize => {
1078 let max_size = get_byte_array::<2>(val).map(u16::from_be_bytes)?;
1079 if max_size < MIN_MESSAGE_SIZE {
1080 return Err(ProtocolError::InvalidOptionValue(code, val.to_vec()));
1081 }
1082 Ok(DhcpOption::MaxDhcpMessageSize(max_size))
1083 }
1084 OptionCode::RenewalTimeValue => {
1085 let renewal_time = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
1086 Ok(DhcpOption::RenewalTimeValue(renewal_time))
1087 }
1088 OptionCode::RebindingTimeValue => {
1089 let rebinding_time = get_byte_array::<4>(val).map(u32::from_be_bytes)?;
1090 Ok(DhcpOption::RebindingTimeValue(rebinding_time))
1091 }
1092 OptionCode::VendorClassIdentifier => {
1093 Ok(DhcpOption::VendorClassIdentifier(val.to_owned().try_into().map_err(
1094 |(size_constrained::Error::SizeConstraintViolated, _)| {
1095 ProtocolError::InvalidBufferLength(val.len())
1096 },
1097 )?))
1098 }
1099 OptionCode::ClientIdentifier => {
1100 if val.len() < 2 {
1103 return Err(ProtocolError::InvalidBufferLength(val.len()));
1104 }
1105 Ok(DhcpOption::ClientIdentifier(val.to_owned().try_into().map_err(
1106 |(size_constrained::Error::SizeConstraintViolated, _)| {
1107 ProtocolError::InvalidBufferLength(val.len())
1108 },
1109 )?))
1110 }
1111 }
1112 }
1113
1114 fn serialize_to(self, buf: &mut Vec<u8>) {
1115 let code = self.code();
1116 match self {
1117 DhcpOption::Pad() => buf.push(code.into()),
1118 DhcpOption::End() => buf.push(code.into()),
1119 DhcpOption::SubnetMask(v) => serialize_address(code, v.get_mask().into(), buf),
1120 DhcpOption::TimeOffset(v) => {
1121 let size = std::mem::size_of::<i32>();
1122 buf.push(code.into());
1123 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1124 buf.extend_from_slice(&v.to_be_bytes());
1125 }
1126 DhcpOption::Router(v) => serialize_addresses(code, &v, buf),
1127 DhcpOption::TimeServer(v) => serialize_addresses(code, &v, buf),
1128 DhcpOption::NameServer(v) => serialize_addresses(code, &v, buf),
1129 DhcpOption::DomainNameServer(v) => serialize_addresses(code, &v, buf),
1130 DhcpOption::LogServer(v) => serialize_addresses(code, &v, buf),
1131 DhcpOption::CookieServer(v) => serialize_addresses(code, &v, buf),
1132 DhcpOption::LprServer(v) => serialize_addresses(code, &v, buf),
1133 DhcpOption::ImpressServer(v) => serialize_addresses(code, &v, buf),
1134 DhcpOption::ResourceLocationServer(v) => serialize_addresses(code, &v, buf),
1135 DhcpOption::HostName(v) => serialize_string(code, &v, buf),
1136 DhcpOption::BootFileSize(v) => serialize_u16(code, v, buf),
1137 DhcpOption::MeritDumpFile(v) => serialize_string(code, &v, buf),
1138 DhcpOption::DomainName(v) => serialize_string(code, &v, buf),
1139 DhcpOption::SwapServer(v) => serialize_address(code, v, buf),
1140 DhcpOption::RootPath(v) => serialize_string(code, &v, buf),
1141 DhcpOption::ExtensionsPath(v) => serialize_string(code, &v, buf),
1142 DhcpOption::IpForwarding(v) => serialize_flag(code, v, buf),
1143 DhcpOption::NonLocalSourceRouting(v) => serialize_flag(code, v, buf),
1144 DhcpOption::PolicyFilter(v) => serialize_addresses(code, &v, buf),
1145 DhcpOption::MaxDatagramReassemblySize(v) => serialize_u16(code, v, buf),
1146 DhcpOption::DefaultIpTtl(v) => serialize_u8(code, v.into(), buf),
1147 DhcpOption::PathMtuAgingTimeout(v) => serialize_u32(code, v, buf),
1148 DhcpOption::PathMtuPlateauTable(v) => {
1149 let size = v.size_of_contents_in_bytes();
1150 buf.push(code.into());
1151 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1152 for mtu in v {
1153 buf.extend_from_slice(&mtu.to_be_bytes())
1154 }
1155 }
1156 DhcpOption::InterfaceMtu(v) => serialize_u16(code, v, buf),
1157 DhcpOption::AllSubnetsLocal(v) => serialize_flag(code, v, buf),
1158 DhcpOption::BroadcastAddress(v) => serialize_address(code, v, buf),
1159 DhcpOption::PerformMaskDiscovery(v) => serialize_flag(code, v, buf),
1160 DhcpOption::MaskSupplier(v) => serialize_flag(code, v, buf),
1161 DhcpOption::PerformRouterDiscovery(v) => serialize_flag(code, v, buf),
1162 DhcpOption::RouterSolicitationAddress(v) => serialize_address(code, v, buf),
1163 DhcpOption::StaticRoute(v) => serialize_addresses(code, &v, buf),
1164 DhcpOption::TrailerEncapsulation(v) => serialize_flag(code, v, buf),
1165 DhcpOption::ArpCacheTimeout(v) => serialize_u32(code, v, buf),
1166 DhcpOption::EthernetEncapsulation(v) => serialize_flag(code, v, buf),
1167 DhcpOption::TcpDefaultTtl(v) => serialize_u8(code, v.into(), buf),
1168 DhcpOption::TcpKeepaliveInterval(v) => serialize_u32(code, v, buf),
1169 DhcpOption::TcpKeepaliveGarbage(v) => serialize_flag(code, v, buf),
1170 DhcpOption::NetworkInformationServiceDomain(v) => serialize_string(code, &v, buf),
1171 DhcpOption::NetworkInformationServers(v) => serialize_addresses(code, &v, buf),
1172 DhcpOption::NetworkTimeProtocolServers(v) => serialize_addresses(code, &v, buf),
1173 DhcpOption::VendorSpecificInformation(v) => serialize_bytes(code, &v, buf),
1174 DhcpOption::NetBiosOverTcpipNameServer(v) => serialize_addresses(code, &v, buf),
1175 DhcpOption::NetBiosOverTcpipDatagramDistributionServer(v) => {
1176 serialize_addresses(code, &v, buf)
1177 }
1178 DhcpOption::NetBiosOverTcpipNodeType(v) => serialize_enum(code, v, buf),
1179 DhcpOption::NetBiosOverTcpipScope(v) => serialize_string(code, &v, buf),
1180 DhcpOption::XWindowSystemFontServer(v) => serialize_addresses(code, &v, buf),
1181 DhcpOption::XWindowSystemDisplayManager(v) => serialize_addresses(code, &v, buf),
1182 DhcpOption::NetworkInformationServicePlusDomain(v) => serialize_string(code, &v, buf),
1183 DhcpOption::NetworkInformationServicePlusServers(v) => {
1184 serialize_addresses(code, &v, buf)
1185 }
1186 DhcpOption::MobileIpHomeAgent(v) => serialize_addresses(code, &v, buf),
1187 DhcpOption::SmtpServer(v) => serialize_addresses(code, &v, buf),
1188 DhcpOption::Pop3Server(v) => serialize_addresses(code, &v, buf),
1189 DhcpOption::NntpServer(v) => serialize_addresses(code, &v, buf),
1190 DhcpOption::DefaultWwwServer(v) => serialize_addresses(code, &v, buf),
1191 DhcpOption::DefaultFingerServer(v) => serialize_addresses(code, &v, buf),
1192 DhcpOption::DefaultIrcServer(v) => serialize_addresses(code, &v, buf),
1193 DhcpOption::StreetTalkServer(v) => serialize_addresses(code, &v, buf),
1194 DhcpOption::StreetTalkDirectoryAssistanceServer(v) => {
1195 serialize_addresses(code, &v, buf)
1196 }
1197 DhcpOption::RequestedIpAddress(v) => serialize_address(code, v, buf),
1198 DhcpOption::IpAddressLeaseTime(v) => serialize_u32(code, v, buf),
1199 DhcpOption::OptionOverload(v) => serialize_enum(code, v, buf),
1200 DhcpOption::TftpServerName(v) => serialize_string(code, &v, buf),
1201 DhcpOption::BootfileName(v) => serialize_string(code, &v, buf),
1202 DhcpOption::DhcpMessageType(v) => serialize_enum(code, v, buf),
1203 DhcpOption::ServerIdentifier(v) => serialize_address(code, v, buf),
1204 DhcpOption::ParameterRequestList(v) => {
1205 let v = Vec::from(v);
1206 let size = v.size_of_contents_in_bytes();
1207 buf.push(code.into());
1208 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1209 buf.extend(v.into_iter().map(u8::from));
1210 }
1211 DhcpOption::Message(v) => serialize_string(code, &v, buf),
1212 DhcpOption::MaxDhcpMessageSize(v) => serialize_u16(code, v, buf),
1213 DhcpOption::RenewalTimeValue(v) => serialize_u32(code, v, buf),
1214 DhcpOption::RebindingTimeValue(v) => serialize_u32(code, v, buf),
1215 DhcpOption::VendorClassIdentifier(v) => serialize_bytes(code, &v, buf),
1216 DhcpOption::ClientIdentifier(v) => serialize_bytes(code, &v, buf),
1217 }
1218 }
1219
1220 pub fn code(&self) -> OptionCode {
1222 option_to_code!(
1223 self,
1224 DhcpOption::Pad(),
1225 DhcpOption::End(),
1226 DhcpOption::SubnetMask(_),
1227 DhcpOption::TimeOffset(_),
1228 DhcpOption::Router(_),
1229 DhcpOption::TimeServer(_),
1230 DhcpOption::NameServer(_),
1231 DhcpOption::DomainNameServer(_),
1232 DhcpOption::LogServer(_),
1233 DhcpOption::CookieServer(_),
1234 DhcpOption::LprServer(_),
1235 DhcpOption::ImpressServer(_),
1236 DhcpOption::ResourceLocationServer(_),
1237 DhcpOption::HostName(_),
1238 DhcpOption::BootFileSize(_),
1239 DhcpOption::MeritDumpFile(_),
1240 DhcpOption::DomainName(_),
1241 DhcpOption::SwapServer(_),
1242 DhcpOption::RootPath(_),
1243 DhcpOption::ExtensionsPath(_),
1244 DhcpOption::IpForwarding(_),
1245 DhcpOption::NonLocalSourceRouting(_),
1246 DhcpOption::PolicyFilter(_),
1247 DhcpOption::MaxDatagramReassemblySize(_),
1248 DhcpOption::DefaultIpTtl(_),
1249 DhcpOption::PathMtuAgingTimeout(_),
1250 DhcpOption::PathMtuPlateauTable(_),
1251 DhcpOption::InterfaceMtu(_),
1252 DhcpOption::AllSubnetsLocal(_),
1253 DhcpOption::BroadcastAddress(_),
1254 DhcpOption::PerformMaskDiscovery(_),
1255 DhcpOption::MaskSupplier(_),
1256 DhcpOption::PerformRouterDiscovery(_),
1257 DhcpOption::RouterSolicitationAddress(_),
1258 DhcpOption::StaticRoute(_),
1259 DhcpOption::TrailerEncapsulation(_),
1260 DhcpOption::ArpCacheTimeout(_),
1261 DhcpOption::EthernetEncapsulation(_),
1262 DhcpOption::TcpDefaultTtl(_),
1263 DhcpOption::TcpKeepaliveInterval(_),
1264 DhcpOption::TcpKeepaliveGarbage(_),
1265 DhcpOption::NetworkInformationServiceDomain(_),
1266 DhcpOption::NetworkInformationServers(_),
1267 DhcpOption::NetworkTimeProtocolServers(_),
1268 DhcpOption::VendorSpecificInformation(_),
1269 DhcpOption::NetBiosOverTcpipNameServer(_),
1270 DhcpOption::NetBiosOverTcpipDatagramDistributionServer(_),
1271 DhcpOption::NetBiosOverTcpipNodeType(_),
1272 DhcpOption::NetBiosOverTcpipScope(_),
1273 DhcpOption::XWindowSystemFontServer(_),
1274 DhcpOption::XWindowSystemDisplayManager(_),
1275 DhcpOption::NetworkInformationServicePlusDomain(_),
1276 DhcpOption::NetworkInformationServicePlusServers(_),
1277 DhcpOption::MobileIpHomeAgent(_),
1278 DhcpOption::SmtpServer(_),
1279 DhcpOption::Pop3Server(_),
1280 DhcpOption::NntpServer(_),
1281 DhcpOption::DefaultWwwServer(_),
1282 DhcpOption::DefaultFingerServer(_),
1283 DhcpOption::DefaultIrcServer(_),
1284 DhcpOption::StreetTalkServer(_),
1285 DhcpOption::StreetTalkDirectoryAssistanceServer(_),
1286 DhcpOption::RequestedIpAddress(_),
1287 DhcpOption::IpAddressLeaseTime(_),
1288 DhcpOption::OptionOverload(_),
1289 DhcpOption::TftpServerName(_),
1290 DhcpOption::BootfileName(_),
1291 DhcpOption::DhcpMessageType(_),
1292 DhcpOption::ServerIdentifier(_),
1293 DhcpOption::ParameterRequestList(_),
1294 DhcpOption::Message(_),
1295 DhcpOption::MaxDhcpMessageSize(_),
1296 DhcpOption::RenewalTimeValue(_),
1297 DhcpOption::RebindingTimeValue(_),
1298 DhcpOption::VendorClassIdentifier(_),
1299 DhcpOption::ClientIdentifier(_)
1300 )
1301 }
1302}
1303
1304fn serialize_address(code: OptionCode, addr: Ipv4Addr, buf: &mut Vec<u8>) {
1305 serialize_addresses(code, &[addr], buf);
1306}
1307
1308fn serialize_addresses(code: OptionCode, addrs: &[Ipv4Addr], buf: &mut Vec<u8>) {
1309 let size = addrs.size_of_contents_in_bytes();
1310 buf.push(code.into());
1311 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1312 for addr in addrs {
1313 buf.extend_from_slice(&addr.octets());
1314 }
1315}
1316
1317fn serialize_string(code: OptionCode, string: &str, buf: &mut Vec<u8>) {
1318 let size = string.len();
1319 buf.push(code.into());
1320 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1321 buf.extend_from_slice(string.as_bytes());
1322}
1323
1324fn serialize_flag(code: OptionCode, flag: bool, buf: &mut Vec<u8>) {
1325 let size = std::mem::size_of::<bool>();
1326 buf.push(code.into());
1327 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1328 buf.push(flag.into());
1329}
1330
1331fn serialize_u16(code: OptionCode, v: u16, buf: &mut Vec<u8>) {
1332 let size = std::mem::size_of::<u16>();
1333 buf.push(code.into());
1334 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1335 buf.extend_from_slice(&v.to_be_bytes());
1336}
1337
1338fn serialize_u8(code: OptionCode, v: u8, buf: &mut Vec<u8>) {
1339 let size = std::mem::size_of::<u8>();
1340 buf.push(code.into());
1341 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1342 buf.push(v);
1343}
1344
1345fn serialize_u32(code: OptionCode, v: u32, buf: &mut Vec<u8>) {
1346 let size = std::mem::size_of::<u32>();
1347 buf.push(code.into());
1348 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1349 buf.extend_from_slice(&v.to_be_bytes());
1350}
1351
1352fn serialize_bytes(code: OptionCode, v: &[u8], buf: &mut Vec<u8>) {
1353 let size = v.size_of_contents_in_bytes();
1354 buf.push(code.into());
1355 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1356 buf.extend_from_slice(v);
1357}
1358
1359fn serialize_enum<T: Into<u8>>(code: OptionCode, v: T, buf: &mut Vec<u8>) {
1360 let size = std::mem::size_of::<T>();
1361 buf.push(code.into());
1362 buf.push(u8::try_from(size).expect("size did not fit in u8"));
1363 buf.push(v.into());
1364}
1365
1366#[cfg(target_os = "fuchsia")]
1368pub trait FidlCompatible<F>: Sized {
1369 type FromError;
1370 type IntoError;
1371
1372 fn try_from_fidl(fidl: F) -> Result<Self, Self::FromError>;
1373 fn try_into_fidl(self) -> Result<F, Self::IntoError>;
1374}
1375
1376#[cfg(target_os = "fuchsia")]
1378pub trait FromFidlExt<F>: FidlCompatible<F, FromError = !> {
1379 fn from_fidl(fidl: F) -> Self {
1380 match Self::try_from_fidl(fidl) {
1381 Ok(slf) => slf,
1382 }
1383 }
1384}
1385
1386#[cfg(target_os = "fuchsia")]
1388pub trait IntoFidlExt<F>: FidlCompatible<F, IntoError = !> {
1389 fn into_fidl(self) -> F {
1390 match self.try_into_fidl() {
1391 Ok(fidl) => fidl,
1392 }
1393 }
1394}
1395
1396#[cfg(target_os = "fuchsia")]
1397impl<F, C: FidlCompatible<F, IntoError = !>> IntoFidlExt<F> for C {}
1398#[cfg(target_os = "fuchsia")]
1399impl<F, C: FidlCompatible<F, FromError = !>> FromFidlExt<F> for C {}
1400
1401#[cfg(target_os = "fuchsia")]
1402impl FidlCompatible<fidl_fuchsia_net::Ipv4Address> for Ipv4Addr {
1403 type FromError = !;
1404 type IntoError = !;
1405
1406 fn try_from_fidl(fidl: fidl_fuchsia_net::Ipv4Address) -> Result<Self, Self::FromError> {
1407 Ok(Ipv4Addr::from(fidl.addr))
1408 }
1409
1410 fn try_into_fidl(self) -> Result<fidl_fuchsia_net::Ipv4Address, Self::IntoError> {
1411 Ok(fidl_fuchsia_net::Ipv4Address { addr: self.octets() })
1412 }
1413}
1414
1415#[cfg(target_os = "fuchsia")]
1416impl FidlCompatible<Vec<fidl_fuchsia_net::Ipv4Address>> for Vec<Ipv4Addr> {
1417 type FromError = !;
1418 type IntoError = !;
1419
1420 fn try_from_fidl(fidl: Vec<fidl_fuchsia_net::Ipv4Address>) -> Result<Self, Self::FromError> {
1421 Ok(fidl
1422 .into_iter()
1423 .filter_map(|addr| Ipv4Addr::try_from_fidl(addr).ok())
1424 .collect::<Vec<Ipv4Addr>>())
1425 }
1426
1427 fn try_into_fidl(self) -> Result<Vec<fidl_fuchsia_net::Ipv4Address>, Self::IntoError> {
1428 Ok(self
1429 .into_iter()
1430 .filter_map(|addr| addr.try_into_fidl().ok())
1431 .collect::<Vec<fidl_fuchsia_net::Ipv4Address>>())
1432 }
1433}
1434
1435#[cfg(target_os = "fuchsia")]
1437impl FidlCompatible<fidl_fuchsia_net_dhcp::Option_> for DhcpOption {
1438 type FromError = ProtocolError;
1439 type IntoError = ProtocolError;
1440
1441 fn try_into_fidl(self) -> Result<fidl_fuchsia_net_dhcp::Option_, Self::IntoError> {
1442 match self {
1443 DhcpOption::Pad() => Err(Self::IntoError::InvalidFidlOption(self)),
1444 DhcpOption::End() => Err(Self::IntoError::InvalidFidlOption(self)),
1445 DhcpOption::SubnetMask(v) => Ok(fidl_fuchsia_net_dhcp::Option_::SubnetMask(
1446 Ipv4Addr::from(v.get_mask()).into_fidl(),
1447 )),
1448 DhcpOption::TimeOffset(v) => Ok(fidl_fuchsia_net_dhcp::Option_::TimeOffset(v)),
1449 DhcpOption::Router(v) => {
1450 Ok(fidl_fuchsia_net_dhcp::Option_::Router(Vec::from(v).into_fidl()))
1451 }
1452 DhcpOption::TimeServer(v) => {
1453 Ok(fidl_fuchsia_net_dhcp::Option_::TimeServer(Vec::from(v).into_fidl()))
1454 }
1455 DhcpOption::NameServer(v) => {
1456 Ok(fidl_fuchsia_net_dhcp::Option_::NameServer(Vec::from(v).into_fidl()))
1457 }
1458 DhcpOption::DomainNameServer(v) => {
1459 Ok(fidl_fuchsia_net_dhcp::Option_::DomainNameServer(Vec::from(v).into_fidl()))
1460 }
1461 DhcpOption::LogServer(v) => {
1462 Ok(fidl_fuchsia_net_dhcp::Option_::LogServer(Vec::from(v).into_fidl()))
1463 }
1464 DhcpOption::CookieServer(v) => {
1465 Ok(fidl_fuchsia_net_dhcp::Option_::CookieServer(Vec::from(v).into_fidl()))
1466 }
1467 DhcpOption::LprServer(v) => {
1468 Ok(fidl_fuchsia_net_dhcp::Option_::LprServer(Vec::from(v).into_fidl()))
1469 }
1470 DhcpOption::ImpressServer(v) => {
1471 Ok(fidl_fuchsia_net_dhcp::Option_::ImpressServer(Vec::from(v).into_fidl()))
1472 }
1473 DhcpOption::ResourceLocationServer(v) => {
1474 Ok(fidl_fuchsia_net_dhcp::Option_::ResourceLocationServer(Vec::from(v).into_fidl()))
1475 }
1476 DhcpOption::HostName(v) => Ok(fidl_fuchsia_net_dhcp::Option_::HostName(v)),
1477 DhcpOption::BootFileSize(v) => Ok(fidl_fuchsia_net_dhcp::Option_::BootFileSize(v)),
1478 DhcpOption::MeritDumpFile(v) => Ok(fidl_fuchsia_net_dhcp::Option_::MeritDumpFile(v)),
1479 DhcpOption::DomainName(v) => Ok(fidl_fuchsia_net_dhcp::Option_::DomainName(v)),
1480 DhcpOption::SwapServer(v) => {
1481 Ok(fidl_fuchsia_net_dhcp::Option_::SwapServer(v.into_fidl()))
1482 }
1483 DhcpOption::RootPath(v) => Ok(fidl_fuchsia_net_dhcp::Option_::RootPath(v)),
1484 DhcpOption::ExtensionsPath(v) => Ok(fidl_fuchsia_net_dhcp::Option_::ExtensionsPath(v)),
1485 DhcpOption::IpForwarding(v) => Ok(fidl_fuchsia_net_dhcp::Option_::IpForwarding(v)),
1486 DhcpOption::NonLocalSourceRouting(v) => {
1487 Ok(fidl_fuchsia_net_dhcp::Option_::NonLocalSourceRouting(v))
1488 }
1489 DhcpOption::PolicyFilter(v) => {
1490 Ok(fidl_fuchsia_net_dhcp::Option_::PolicyFilter(Vec::from(v).into_fidl()))
1491 }
1492 DhcpOption::MaxDatagramReassemblySize(v) => {
1493 Ok(fidl_fuchsia_net_dhcp::Option_::MaxDatagramReassemblySize(v))
1494 }
1495 DhcpOption::DefaultIpTtl(v) => {
1496 Ok(fidl_fuchsia_net_dhcp::Option_::DefaultIpTtl(v.into()))
1497 }
1498 DhcpOption::PathMtuAgingTimeout(v) => {
1499 Ok(fidl_fuchsia_net_dhcp::Option_::PathMtuAgingTimeout(v))
1500 }
1501 DhcpOption::PathMtuPlateauTable(v) => {
1502 Ok(fidl_fuchsia_net_dhcp::Option_::PathMtuPlateauTable(v.into()))
1503 }
1504 DhcpOption::InterfaceMtu(v) => Ok(fidl_fuchsia_net_dhcp::Option_::InterfaceMtu(v)),
1505 DhcpOption::AllSubnetsLocal(v) => {
1506 Ok(fidl_fuchsia_net_dhcp::Option_::AllSubnetsLocal(v))
1507 }
1508 DhcpOption::BroadcastAddress(v) => {
1509 Ok(fidl_fuchsia_net_dhcp::Option_::BroadcastAddress(v.into_fidl()))
1510 }
1511 DhcpOption::PerformMaskDiscovery(v) => {
1512 Ok(fidl_fuchsia_net_dhcp::Option_::PerformMaskDiscovery(v))
1513 }
1514 DhcpOption::MaskSupplier(v) => Ok(fidl_fuchsia_net_dhcp::Option_::MaskSupplier(v)),
1515 DhcpOption::PerformRouterDiscovery(v) => {
1516 Ok(fidl_fuchsia_net_dhcp::Option_::PerformRouterDiscovery(v))
1517 }
1518 DhcpOption::RouterSolicitationAddress(v) => {
1519 Ok(fidl_fuchsia_net_dhcp::Option_::RouterSolicitationAddress(v.into_fidl()))
1520 }
1521 DhcpOption::StaticRoute(v) => {
1522 Ok(fidl_fuchsia_net_dhcp::Option_::StaticRoute(Vec::from(v).into_fidl()))
1523 }
1524 DhcpOption::TrailerEncapsulation(v) => {
1525 Ok(fidl_fuchsia_net_dhcp::Option_::TrailerEncapsulation(v))
1526 }
1527 DhcpOption::ArpCacheTimeout(v) => {
1528 Ok(fidl_fuchsia_net_dhcp::Option_::ArpCacheTimeout(v))
1529 }
1530 DhcpOption::EthernetEncapsulation(v) => {
1531 Ok(fidl_fuchsia_net_dhcp::Option_::EthernetEncapsulation(v))
1532 }
1533 DhcpOption::TcpDefaultTtl(v) => {
1534 Ok(fidl_fuchsia_net_dhcp::Option_::TcpDefaultTtl(v.into()))
1535 }
1536 DhcpOption::TcpKeepaliveInterval(v) => {
1537 Ok(fidl_fuchsia_net_dhcp::Option_::TcpKeepaliveInterval(v))
1538 }
1539 DhcpOption::TcpKeepaliveGarbage(v) => {
1540 Ok(fidl_fuchsia_net_dhcp::Option_::TcpKeepaliveGarbage(v))
1541 }
1542 DhcpOption::NetworkInformationServiceDomain(v) => {
1543 Ok(fidl_fuchsia_net_dhcp::Option_::NetworkInformationServiceDomain(v))
1544 }
1545 DhcpOption::NetworkInformationServers(v) => Ok(
1546 fidl_fuchsia_net_dhcp::Option_::NetworkInformationServers(Vec::from(v).into_fidl()),
1547 ),
1548 DhcpOption::NetworkTimeProtocolServers(v) => {
1549 Ok(fidl_fuchsia_net_dhcp::Option_::NetworkTimeProtocolServers(
1550 Vec::from(v).into_fidl(),
1551 ))
1552 }
1553 DhcpOption::VendorSpecificInformation(v) => {
1554 Ok(fidl_fuchsia_net_dhcp::Option_::VendorSpecificInformation(v.into()))
1555 }
1556 DhcpOption::NetBiosOverTcpipNameServer(v) => {
1557 Ok(fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipNameServer(
1558 Vec::from(v).into_fidl(),
1559 ))
1560 }
1561 DhcpOption::NetBiosOverTcpipDatagramDistributionServer(v) => {
1562 Ok(fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipDatagramDistributionServer(
1563 Vec::from(v).into_fidl(),
1564 ))
1565 }
1566 DhcpOption::NetBiosOverTcpipNodeType(v) => {
1567 Ok(fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipNodeType(v.into_fidl()))
1568 }
1569 DhcpOption::NetBiosOverTcpipScope(v) => {
1570 Ok(fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipScope(v))
1571 }
1572 DhcpOption::XWindowSystemFontServer(v) => Ok(
1573 fidl_fuchsia_net_dhcp::Option_::XWindowSystemFontServer(Vec::from(v).into_fidl()),
1574 ),
1575 DhcpOption::XWindowSystemDisplayManager(v) => {
1576 Ok(fidl_fuchsia_net_dhcp::Option_::XWindowSystemDisplayManager(
1577 Vec::from(v).into_fidl(),
1578 ))
1579 }
1580 DhcpOption::NetworkInformationServicePlusDomain(v) => {
1581 Ok(fidl_fuchsia_net_dhcp::Option_::NetworkInformationServicePlusDomain(v))
1582 }
1583 DhcpOption::NetworkInformationServicePlusServers(v) => {
1584 Ok(fidl_fuchsia_net_dhcp::Option_::NetworkInformationServicePlusServers(
1585 Vec::from(v).into_fidl(),
1586 ))
1587 }
1588 DhcpOption::MobileIpHomeAgent(v) => {
1589 Ok(fidl_fuchsia_net_dhcp::Option_::MobileIpHomeAgent(Vec::from(v).into_fidl()))
1590 }
1591 DhcpOption::SmtpServer(v) => {
1592 Ok(fidl_fuchsia_net_dhcp::Option_::SmtpServer(Vec::from(v).into_fidl()))
1593 }
1594 DhcpOption::Pop3Server(v) => {
1595 Ok(fidl_fuchsia_net_dhcp::Option_::Pop3Server(Vec::from(v).into_fidl()))
1596 }
1597 DhcpOption::NntpServer(v) => {
1598 Ok(fidl_fuchsia_net_dhcp::Option_::NntpServer(Vec::from(v).into_fidl()))
1599 }
1600 DhcpOption::DefaultWwwServer(v) => {
1601 Ok(fidl_fuchsia_net_dhcp::Option_::DefaultWwwServer(Vec::from(v).into_fidl()))
1602 }
1603 DhcpOption::DefaultFingerServer(v) => {
1604 Ok(fidl_fuchsia_net_dhcp::Option_::DefaultFingerServer(Vec::from(v).into_fidl()))
1605 }
1606 DhcpOption::DefaultIrcServer(v) => {
1607 Ok(fidl_fuchsia_net_dhcp::Option_::DefaultIrcServer(Vec::from(v).into_fidl()))
1608 }
1609 DhcpOption::StreetTalkServer(v) => {
1610 Ok(fidl_fuchsia_net_dhcp::Option_::StreettalkServer(Vec::from(v).into_fidl()))
1611 }
1612 DhcpOption::StreetTalkDirectoryAssistanceServer(v) => {
1613 Ok(fidl_fuchsia_net_dhcp::Option_::StreettalkDirectoryAssistanceServer(
1614 Vec::from(v).into_fidl(),
1615 ))
1616 }
1617 DhcpOption::RequestedIpAddress(_) => Err(ProtocolError::InvalidFidlOption(self)),
1618 DhcpOption::IpAddressLeaseTime(_) => Err(ProtocolError::InvalidFidlOption(self)),
1619 DhcpOption::OptionOverload(v) => {
1620 Ok(fidl_fuchsia_net_dhcp::Option_::OptionOverload(v.into_fidl()))
1621 }
1622 DhcpOption::TftpServerName(v) => Ok(fidl_fuchsia_net_dhcp::Option_::TftpServerName(v)),
1623 DhcpOption::BootfileName(v) => Ok(fidl_fuchsia_net_dhcp::Option_::BootfileName(v)),
1624 DhcpOption::DhcpMessageType(_) => Err(ProtocolError::InvalidFidlOption(self)),
1625 DhcpOption::ServerIdentifier(_) => Err(ProtocolError::InvalidFidlOption(self)),
1626 DhcpOption::ParameterRequestList(_) => Err(ProtocolError::InvalidFidlOption(self)),
1627 DhcpOption::Message(_) => Err(ProtocolError::InvalidFidlOption(self)),
1628 DhcpOption::MaxDhcpMessageSize(v) => {
1629 Ok(fidl_fuchsia_net_dhcp::Option_::MaxDhcpMessageSize(v))
1630 }
1631 DhcpOption::RenewalTimeValue(v) => {
1632 Ok(fidl_fuchsia_net_dhcp::Option_::RenewalTimeValue(v))
1633 }
1634 DhcpOption::RebindingTimeValue(v) => {
1635 Ok(fidl_fuchsia_net_dhcp::Option_::RebindingTimeValue(v))
1636 }
1637 DhcpOption::VendorClassIdentifier(_) => Err(ProtocolError::InvalidFidlOption(self)),
1638 DhcpOption::ClientIdentifier(_) => Err(ProtocolError::InvalidFidlOption(self)),
1639 }
1640 }
1641
1642 fn try_from_fidl(v: fidl_fuchsia_net_dhcp::Option_) -> Result<Self, Self::FromError> {
1643 match v {
1644 fidl_fuchsia_net_dhcp::Option_::SubnetMask(v) => {
1645 let addr = Ipv4Addr::from_fidl(v);
1646 Ok(DhcpOption::SubnetMask(
1647 PrefixLength::try_from_subnet_mask(addr.into()).map_err(
1648 |NotSubnetMaskError| {
1649 ProtocolError::InvalidOptionValue(
1650 OptionCode::SubnetMask,
1651 addr.octets().to_vec(),
1652 )
1653 },
1654 )?,
1655 ))
1656 }
1657 fidl_fuchsia_net_dhcp::Option_::TimeOffset(v) => Ok(DhcpOption::TimeOffset(v)),
1658 fidl_fuchsia_net_dhcp::Option_::Router(v) => Ok(DhcpOption::Router({
1659 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1660 let size = vec.size_of_contents_in_bytes();
1661 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1662 ProtocolError::InvalidBufferLength(size)
1663 })?
1664 })),
1665 fidl_fuchsia_net_dhcp::Option_::TimeServer(v) => Ok(DhcpOption::TimeServer({
1666 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1667 let size = vec.size_of_contents_in_bytes();
1668 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1669 ProtocolError::InvalidBufferLength(size)
1670 })?
1671 })),
1672 fidl_fuchsia_net_dhcp::Option_::NameServer(v) => Ok(DhcpOption::NameServer({
1673 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1674 let size = vec.size_of_contents_in_bytes();
1675 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1676 ProtocolError::InvalidBufferLength(size)
1677 })?
1678 })),
1679 fidl_fuchsia_net_dhcp::Option_::DomainNameServer(v) => {
1680 Ok(DhcpOption::DomainNameServer({
1681 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1682 let vec_size = vec.size_of_contents_in_bytes();
1683 vec.try_into().map_err(
1684 |(size_constrained::Error::SizeConstraintViolated, _)| {
1685 ProtocolError::InvalidBufferLength(vec_size)
1686 },
1687 )?
1688 }))
1689 }
1690 fidl_fuchsia_net_dhcp::Option_::LogServer(v) => Ok(DhcpOption::LogServer({
1691 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1692 let size = vec.size_of_contents_in_bytes();
1693 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1694 ProtocolError::InvalidBufferLength(size)
1695 })?
1696 })),
1697 fidl_fuchsia_net_dhcp::Option_::CookieServer(v) => Ok(DhcpOption::CookieServer({
1698 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1699 let size = vec.size_of_contents_in_bytes();
1700 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1701 ProtocolError::InvalidBufferLength(size)
1702 })?
1703 })),
1704 fidl_fuchsia_net_dhcp::Option_::LprServer(v) => Ok(DhcpOption::LprServer({
1705 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1706 let size = vec.size_of_contents_in_bytes();
1707 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1708 ProtocolError::InvalidBufferLength(size)
1709 })?
1710 })),
1711 fidl_fuchsia_net_dhcp::Option_::ImpressServer(v) => Ok(DhcpOption::ImpressServer({
1712 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1713 let size = vec.size_of_contents_in_bytes();
1714 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1715 ProtocolError::InvalidBufferLength(size)
1716 })?
1717 })),
1718 fidl_fuchsia_net_dhcp::Option_::ResourceLocationServer(v) => {
1719 Ok(DhcpOption::ResourceLocationServer({
1720 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1721 let vec_size = vec.size_of_contents_in_bytes();
1722 vec.try_into().map_err(
1723 |(size_constrained::Error::SizeConstraintViolated, _)| {
1724 ProtocolError::InvalidBufferLength(vec_size)
1725 },
1726 )?
1727 }))
1728 }
1729 fidl_fuchsia_net_dhcp::Option_::HostName(v) => Ok(DhcpOption::HostName(v)),
1730 fidl_fuchsia_net_dhcp::Option_::BootFileSize(v) => Ok(DhcpOption::BootFileSize(v)),
1731 fidl_fuchsia_net_dhcp::Option_::MeritDumpFile(v) => Ok(DhcpOption::MeritDumpFile(v)),
1732 fidl_fuchsia_net_dhcp::Option_::DomainName(v) => Ok(DhcpOption::DomainName(v)),
1733 fidl_fuchsia_net_dhcp::Option_::SwapServer(v) => {
1734 Ok(DhcpOption::SwapServer(Ipv4Addr::from_fidl(v)))
1735 }
1736 fidl_fuchsia_net_dhcp::Option_::RootPath(v) => Ok(DhcpOption::RootPath(v)),
1737 fidl_fuchsia_net_dhcp::Option_::ExtensionsPath(v) => Ok(DhcpOption::ExtensionsPath(v)),
1738 fidl_fuchsia_net_dhcp::Option_::IpForwarding(v) => Ok(DhcpOption::IpForwarding(v)),
1739 fidl_fuchsia_net_dhcp::Option_::NonLocalSourceRouting(v) => {
1740 Ok(DhcpOption::NonLocalSourceRouting(v))
1741 }
1742 fidl_fuchsia_net_dhcp::Option_::PolicyFilter(v) => Ok(DhcpOption::PolicyFilter({
1743 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1744 let size = vec.size_of_contents_in_bytes();
1745 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1746 ProtocolError::InvalidBufferLength(size)
1747 })?
1748 })),
1749 fidl_fuchsia_net_dhcp::Option_::MaxDatagramReassemblySize(v) => {
1750 Ok(DhcpOption::MaxDatagramReassemblySize(v))
1751 }
1752 fidl_fuchsia_net_dhcp::Option_::DefaultIpTtl(v) => {
1753 let ttl = NonZeroU8::new(v).ok_or_else(|| {
1754 ProtocolError::InvalidOptionValue(OptionCode::DefaultIpTtl, vec![v])
1755 })?;
1756 Ok(DhcpOption::DefaultIpTtl(ttl))
1757 }
1758 fidl_fuchsia_net_dhcp::Option_::PathMtuAgingTimeout(v) => {
1759 Ok(DhcpOption::PathMtuAgingTimeout(v))
1760 }
1761 fidl_fuchsia_net_dhcp::Option_::PathMtuPlateauTable(v) => {
1762 Ok(DhcpOption::PathMtuPlateauTable({
1763 let size = v.size_of_contents_in_bytes();
1764 v.try_into().map_err(
1765 |(size_constrained::Error::SizeConstraintViolated, _)| {
1766 ProtocolError::InvalidBufferLength(size)
1767 },
1768 )?
1769 }))
1770 }
1771 fidl_fuchsia_net_dhcp::Option_::InterfaceMtu(v) => Ok(DhcpOption::InterfaceMtu(v)),
1772 fidl_fuchsia_net_dhcp::Option_::AllSubnetsLocal(v) => {
1773 Ok(DhcpOption::AllSubnetsLocal(v))
1774 }
1775 fidl_fuchsia_net_dhcp::Option_::BroadcastAddress(v) => {
1776 Ok(DhcpOption::BroadcastAddress(Ipv4Addr::from_fidl(v)))
1777 }
1778 fidl_fuchsia_net_dhcp::Option_::PerformMaskDiscovery(v) => {
1779 Ok(DhcpOption::PerformMaskDiscovery(v))
1780 }
1781 fidl_fuchsia_net_dhcp::Option_::MaskSupplier(v) => Ok(DhcpOption::MaskSupplier(v)),
1782 fidl_fuchsia_net_dhcp::Option_::PerformRouterDiscovery(v) => {
1783 Ok(DhcpOption::PerformRouterDiscovery(v))
1784 }
1785 fidl_fuchsia_net_dhcp::Option_::RouterSolicitationAddress(v) => {
1786 Ok(DhcpOption::RouterSolicitationAddress(Ipv4Addr::from_fidl(v)))
1787 }
1788 fidl_fuchsia_net_dhcp::Option_::StaticRoute(v) => Ok(DhcpOption::StaticRoute({
1789 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1790 let size = vec.size_of_contents_in_bytes();
1791 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1792 ProtocolError::InvalidBufferLength(size)
1793 })?
1794 })),
1795 fidl_fuchsia_net_dhcp::Option_::TrailerEncapsulation(v) => {
1796 Ok(DhcpOption::TrailerEncapsulation(v))
1797 }
1798 fidl_fuchsia_net_dhcp::Option_::ArpCacheTimeout(v) => {
1799 Ok(DhcpOption::ArpCacheTimeout(v))
1800 }
1801 fidl_fuchsia_net_dhcp::Option_::EthernetEncapsulation(v) => {
1802 Ok(DhcpOption::EthernetEncapsulation(v))
1803 }
1804 fidl_fuchsia_net_dhcp::Option_::TcpDefaultTtl(v) => {
1805 let ttl = NonZeroU8::new(v).ok_or_else(|| {
1806 ProtocolError::InvalidOptionValue(OptionCode::TcpDefaultTtl, vec![v])
1807 })?;
1808 Ok(DhcpOption::TcpDefaultTtl(ttl))
1809 }
1810 fidl_fuchsia_net_dhcp::Option_::TcpKeepaliveInterval(v) => {
1811 Ok(DhcpOption::TcpKeepaliveInterval(v))
1812 }
1813 fidl_fuchsia_net_dhcp::Option_::TcpKeepaliveGarbage(v) => {
1814 Ok(DhcpOption::TcpKeepaliveGarbage(v))
1815 }
1816 fidl_fuchsia_net_dhcp::Option_::NetworkInformationServiceDomain(v) => {
1817 Ok(DhcpOption::NetworkInformationServiceDomain(v))
1818 }
1819 fidl_fuchsia_net_dhcp::Option_::NetworkInformationServers(v) => {
1820 Ok(DhcpOption::NetworkInformationServers({
1821 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1822 let vec_size = vec.size_of_contents_in_bytes();
1823 vec.try_into().map_err(
1824 |(size_constrained::Error::SizeConstraintViolated, _)| {
1825 ProtocolError::InvalidBufferLength(vec_size)
1826 },
1827 )?
1828 }))
1829 }
1830 fidl_fuchsia_net_dhcp::Option_::NetworkTimeProtocolServers(v) => {
1831 Ok(DhcpOption::NetworkTimeProtocolServers({
1832 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1833 let vec_size = vec.size_of_contents_in_bytes();
1834 vec.try_into().map_err(
1835 |(size_constrained::Error::SizeConstraintViolated, _)| {
1836 ProtocolError::InvalidBufferLength(vec_size)
1837 },
1838 )?
1839 }))
1840 }
1841 fidl_fuchsia_net_dhcp::Option_::VendorSpecificInformation(v) => {
1842 Ok(DhcpOption::VendorSpecificInformation({
1843 let size = v.size_of_contents_in_bytes();
1844 v.try_into().map_err(
1845 |(size_constrained::Error::SizeConstraintViolated, _)| {
1846 ProtocolError::InvalidBufferLength(size)
1847 },
1848 )?
1849 }))
1850 }
1851 fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipNameServer(v) => {
1852 Ok(DhcpOption::NetBiosOverTcpipNameServer({
1853 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1854 let vec_size = vec.size_of_contents_in_bytes();
1855 vec.try_into().map_err(
1856 |(size_constrained::Error::SizeConstraintViolated, _)| {
1857 ProtocolError::InvalidBufferLength(vec_size)
1858 },
1859 )?
1860 }))
1861 }
1862 fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipDatagramDistributionServer(v) => {
1863 Ok(DhcpOption::NetBiosOverTcpipDatagramDistributionServer({
1864 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1865 let vec_size = vec.size_of_contents_in_bytes();
1866 vec.try_into().map_err(
1867 |(size_constrained::Error::SizeConstraintViolated, _)| {
1868 ProtocolError::InvalidBufferLength(vec_size)
1869 },
1870 )?
1871 }))
1872 }
1873 fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipNodeType(v) => {
1874 Ok(DhcpOption::NetBiosOverTcpipNodeType(NodeType::try_from_fidl(v)?))
1875 }
1876 fidl_fuchsia_net_dhcp::Option_::NetbiosOverTcpipScope(v) => {
1877 Ok(DhcpOption::NetBiosOverTcpipScope(v))
1878 }
1879 fidl_fuchsia_net_dhcp::Option_::XWindowSystemFontServer(v) => {
1880 Ok(DhcpOption::XWindowSystemFontServer({
1881 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1882 let vec_size = vec.size_of_contents_in_bytes();
1883 vec.try_into().map_err(
1884 |(size_constrained::Error::SizeConstraintViolated, _)| {
1885 ProtocolError::InvalidBufferLength(vec_size)
1886 },
1887 )?
1888 }))
1889 }
1890 fidl_fuchsia_net_dhcp::Option_::XWindowSystemDisplayManager(v) => {
1891 Ok(DhcpOption::XWindowSystemDisplayManager({
1892 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1893 let vec_size = vec.size_of_contents_in_bytes();
1894 vec.try_into().map_err(
1895 |(size_constrained::Error::SizeConstraintViolated, _)| {
1896 ProtocolError::InvalidBufferLength(vec_size)
1897 },
1898 )?
1899 }))
1900 }
1901 fidl_fuchsia_net_dhcp::Option_::NetworkInformationServicePlusDomain(v) => {
1902 Ok(DhcpOption::NetworkInformationServicePlusDomain(v))
1903 }
1904 fidl_fuchsia_net_dhcp::Option_::NetworkInformationServicePlusServers(v) => {
1905 Ok(DhcpOption::NetworkInformationServicePlusServers({
1906 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1907 let vec_size = vec.size_of_contents_in_bytes();
1908 vec.try_into().map_err(
1909 |(size_constrained::Error::SizeConstraintViolated, _)| {
1910 ProtocolError::InvalidBufferLength(vec_size)
1911 },
1912 )?
1913 }))
1914 }
1915 fidl_fuchsia_net_dhcp::Option_::MobileIpHomeAgent(v) => {
1916 Ok(DhcpOption::MobileIpHomeAgent({
1917 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1918 let size = vec.size_of_contents_in_bytes();
1919 vec.try_into().map_err(
1920 |(size_constrained::Error::SizeConstraintViolated, _)| {
1921 ProtocolError::InvalidBufferLength(size)
1922 },
1923 )?
1924 }))
1925 }
1926 fidl_fuchsia_net_dhcp::Option_::SmtpServer(v) => Ok(DhcpOption::SmtpServer({
1927 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1928 let size = vec.size_of_contents_in_bytes();
1929 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1930 ProtocolError::InvalidBufferLength(size)
1931 })?
1932 })),
1933 fidl_fuchsia_net_dhcp::Option_::Pop3Server(v) => Ok(DhcpOption::Pop3Server({
1934 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1935 let size = vec.size_of_contents_in_bytes();
1936 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1937 ProtocolError::InvalidBufferLength(size)
1938 })?
1939 })),
1940 fidl_fuchsia_net_dhcp::Option_::NntpServer(v) => Ok(DhcpOption::NntpServer({
1941 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1942 let size = vec.size_of_contents_in_bytes();
1943 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
1944 ProtocolError::InvalidBufferLength(size)
1945 })?
1946 })),
1947 fidl_fuchsia_net_dhcp::Option_::DefaultWwwServer(v) => {
1948 Ok(DhcpOption::DefaultWwwServer({
1949 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1950 let vec_size = vec.size_of_contents_in_bytes();
1951 vec.try_into().map_err(
1952 |(size_constrained::Error::SizeConstraintViolated, _)| {
1953 ProtocolError::InvalidBufferLength(vec_size)
1954 },
1955 )?
1956 }))
1957 }
1958 fidl_fuchsia_net_dhcp::Option_::DefaultFingerServer(v) => {
1959 Ok(DhcpOption::DefaultFingerServer({
1960 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1961 let vec_size = vec.size_of_contents_in_bytes();
1962 vec.try_into().map_err(
1963 |(size_constrained::Error::SizeConstraintViolated, _)| {
1964 ProtocolError::InvalidBufferLength(vec_size)
1965 },
1966 )?
1967 }))
1968 }
1969 fidl_fuchsia_net_dhcp::Option_::DefaultIrcServer(v) => {
1970 Ok(DhcpOption::DefaultIrcServer({
1971 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1972 let vec_size = vec.size_of_contents_in_bytes();
1973 vec.try_into().map_err(
1974 |(size_constrained::Error::SizeConstraintViolated, _)| {
1975 ProtocolError::InvalidBufferLength(vec_size)
1976 },
1977 )?
1978 }))
1979 }
1980 fidl_fuchsia_net_dhcp::Option_::StreettalkServer(v) => {
1981 Ok(DhcpOption::StreetTalkServer({
1982 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1983 let vec_size = vec.size_of_contents_in_bytes();
1984 vec.try_into().map_err(
1985 |(size_constrained::Error::SizeConstraintViolated, _)| {
1986 ProtocolError::InvalidBufferLength(vec_size)
1987 },
1988 )?
1989 }))
1990 }
1991 fidl_fuchsia_net_dhcp::Option_::StreettalkDirectoryAssistanceServer(v) => {
1992 Ok(DhcpOption::StreetTalkDirectoryAssistanceServer({
1993 let vec = Vec::<Ipv4Addr>::from_fidl(v);
1994 let vec_size = vec.size_of_contents_in_bytes();
1995 vec.try_into().map_err(
1996 |(size_constrained::Error::SizeConstraintViolated, _)| {
1997 ProtocolError::InvalidBufferLength(vec_size)
1998 },
1999 )?
2000 }))
2001 }
2002 fidl_fuchsia_net_dhcp::Option_::OptionOverload(v) => {
2003 Ok(DhcpOption::OptionOverload(Overload::from_fidl(v)))
2004 }
2005 fidl_fuchsia_net_dhcp::Option_::TftpServerName(v) => Ok(DhcpOption::TftpServerName(v)),
2006 fidl_fuchsia_net_dhcp::Option_::BootfileName(v) => Ok(DhcpOption::BootfileName(v)),
2007 fidl_fuchsia_net_dhcp::Option_::MaxDhcpMessageSize(v) => {
2008 Ok(DhcpOption::MaxDhcpMessageSize(v))
2009 }
2010 fidl_fuchsia_net_dhcp::Option_::RenewalTimeValue(v) => {
2011 Ok(DhcpOption::RenewalTimeValue(v))
2012 }
2013 fidl_fuchsia_net_dhcp::Option_::RebindingTimeValue(v) => {
2014 Ok(DhcpOption::RebindingTimeValue(v))
2015 }
2016 fidl_fuchsia_net_dhcp::Option_Unknown!() => Err(ProtocolError::UnknownFidlOption),
2017 }
2018 }
2019}
2020
2021#[derive(Clone, Copy, Debug, Deserialize, Eq, FromPrimitive, Hash, PartialEq, Serialize)]
2026#[repr(u8)]
2027pub enum NodeType {
2028 BNode = 0x1,
2029 PNode = 0x2,
2030 MNode = 0x4,
2031 HNode = 0x8,
2032}
2033
2034impl TryFrom<u8> for NodeType {
2035 type Error = ProtocolError;
2036
2037 fn try_from(n: u8) -> Result<Self, Self::Error> {
2038 <Self as num_traits::FromPrimitive>::from_u8(n).ok_or_else(|| {
2039 ProtocolError::InvalidOptionValue(OptionCode::NetBiosOverTcpipNodeType, vec![n])
2040 })
2041 }
2042}
2043
2044impl From<NodeType> for u8 {
2045 fn from(node_type: NodeType) -> u8 {
2046 node_type as u8
2047 }
2048}
2049
2050#[cfg(target_os = "fuchsia")]
2051impl FidlCompatible<fidl_fuchsia_net_dhcp::NodeTypes> for NodeType {
2052 type FromError = ProtocolError;
2053 type IntoError = !;
2054
2055 fn try_from_fidl(fidl: fidl_fuchsia_net_dhcp::NodeTypes) -> Result<NodeType, Self::FromError> {
2056 match fidl {
2057 fidl_fuchsia_net_dhcp::NodeTypes::B_NODE => Ok(NodeType::BNode),
2058 fidl_fuchsia_net_dhcp::NodeTypes::P_NODE => Ok(NodeType::PNode),
2059 fidl_fuchsia_net_dhcp::NodeTypes::M_NODE => Ok(NodeType::MNode),
2060 fidl_fuchsia_net_dhcp::NodeTypes::H_NODE => Ok(NodeType::HNode),
2061 other => Err(ProtocolError::InvalidOptionValue(
2062 OptionCode::NetBiosOverTcpipNodeType,
2063 vec![other.bits()],
2064 )),
2065 }
2066 }
2067
2068 fn try_into_fidl(self) -> Result<fidl_fuchsia_net_dhcp::NodeTypes, Self::IntoError> {
2069 match self {
2070 NodeType::BNode => Ok(fidl_fuchsia_net_dhcp::NodeTypes::B_NODE),
2071 NodeType::PNode => Ok(fidl_fuchsia_net_dhcp::NodeTypes::P_NODE),
2072 NodeType::MNode => Ok(fidl_fuchsia_net_dhcp::NodeTypes::M_NODE),
2073 NodeType::HNode => Ok(fidl_fuchsia_net_dhcp::NodeTypes::H_NODE),
2074 }
2075 }
2076}
2077
2078#[derive(Clone, Copy, Debug, Deserialize, Eq, FromPrimitive, Hash, PartialEq, Serialize)]
2084#[repr(u8)]
2085pub enum Overload {
2086 File = 1,
2087 SName = 2,
2088 Both = 3,
2089}
2090
2091impl From<Overload> for u8 {
2092 fn from(val: Overload) -> Self {
2093 val as u8
2094 }
2095}
2096
2097impl TryFrom<u8> for Overload {
2098 type Error = ProtocolError;
2099
2100 fn try_from(n: u8) -> Result<Self, Self::Error> {
2101 <Self as num_traits::FromPrimitive>::from_u8(n)
2102 .ok_or_else(|| ProtocolError::InvalidOptionValue(OptionCode::OptionOverload, vec![n]))
2103 }
2104}
2105
2106#[cfg(target_os = "fuchsia")]
2107impl FidlCompatible<fidl_fuchsia_net_dhcp::OptionOverloadValue> for Overload {
2108 type FromError = !;
2109 type IntoError = !;
2110
2111 fn try_from_fidl(
2112 fidl: fidl_fuchsia_net_dhcp::OptionOverloadValue,
2113 ) -> Result<Self, Self::FromError> {
2114 match fidl {
2115 fidl_fuchsia_net_dhcp::OptionOverloadValue::File => Ok(Overload::File),
2116 fidl_fuchsia_net_dhcp::OptionOverloadValue::Sname => Ok(Overload::SName),
2117 fidl_fuchsia_net_dhcp::OptionOverloadValue::Both => Ok(Overload::Both),
2118 }
2119 }
2120
2121 fn try_into_fidl(self) -> Result<fidl_fuchsia_net_dhcp::OptionOverloadValue, Self::IntoError> {
2122 match self {
2123 Overload::File => Ok(fidl_fuchsia_net_dhcp::OptionOverloadValue::File),
2124 Overload::SName => Ok(fidl_fuchsia_net_dhcp::OptionOverloadValue::Sname),
2125 Overload::Both => Ok(fidl_fuchsia_net_dhcp::OptionOverloadValue::Both),
2126 }
2127 }
2128}
2129
2130#[derive(FromPrimitive, Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
2135#[repr(u8)]
2136pub enum MessageType {
2137 DHCPDISCOVER = 1,
2138 DHCPOFFER = 2,
2139 DHCPREQUEST = 3,
2140 DHCPDECLINE = 4,
2141 DHCPACK = 5,
2142 DHCPNAK = 6,
2143 DHCPRELEASE = 7,
2144 DHCPINFORM = 8,
2145}
2146
2147impl From<MessageType> for u8 {
2148 fn from(val: MessageType) -> Self {
2149 val as u8
2150 }
2151}
2152
2153impl fmt::Display for MessageType {
2161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2162 fmt::Debug::fmt(&self, f)
2163 }
2164}
2165
2166impl TryFrom<u8> for MessageType {
2167 type Error = ProtocolError;
2168
2169 fn try_from(n: u8) -> Result<Self, Self::Error> {
2170 <Self as num_traits::FromPrimitive>::from_u8(n).ok_or(ProtocolError::InvalidMessageType(n))
2171 }
2172}
2173
2174fn parse_options<T: Extend<DhcpOption>>(
2176 mut buf: &[u8],
2177 mut options: T,
2178) -> Result<T, ProtocolError> {
2179 loop {
2180 let (raw_opt_code, rest) = buf.split_first().ok_or({
2181 ProtocolError::MissingOption(OptionCode::End)
2184 })?;
2185 buf = rest;
2186 match OptionCode::try_from(*raw_opt_code) {
2187 Ok(OptionCode::End) => {
2188 return Ok(options);
2190 }
2191 Ok(OptionCode::Pad) => {}
2192 code => {
2193 let (&opt_len, rest) = buf.split_first().ok_or(ProtocolError::MalformedOption {
2194 code: *raw_opt_code,
2195 remaining: buf.len(),
2196 want: 1,
2197 })?;
2198 buf = rest;
2199 let opt_len = usize::from(opt_len);
2200
2201 if buf.len() < opt_len {
2203 return Err(ProtocolError::MalformedOption {
2204 code: *raw_opt_code,
2205 remaining: buf.len(),
2206 want: opt_len,
2207 });
2208 };
2209 let (val, rest) = buf.split_at(opt_len);
2210 buf = rest;
2211
2212 let code = match code {
2217 Ok(c) => c,
2218 Err(ProtocolError::InvalidOptionCode(_)) => continue,
2219 Err(e) => return Err(e),
2220 };
2221
2222 match DhcpOption::from_raw_parts(code, val) {
2223 Ok(option) => options.extend(std::iter::once(option)),
2224 Err(e) => {
2225 debug!("error while parsing option: {}", e);
2234 continue;
2235 }
2236 }
2237 }
2238 }
2239 }
2240}
2241
2242fn nonempty<T>(slice: &[T]) -> Result<&[T], InvalidBufferLengthError> {
2244 if slice.len() == 0 {
2245 return Err(InvalidBufferLengthError(slice.len()));
2246 }
2247 Ok(slice)
2248}
2249
2250fn get_byte_array<const T: usize>(bytes: &[u8]) -> Result<[u8; T], InvalidBufferLengthError> {
2251 bytes
2252 .try_into()
2253 .map_err(|std::array::TryFromSliceError { .. }| InvalidBufferLengthError(bytes.len()))
2254}
2255
2256fn get_byte(bytes: &[u8]) -> Result<u8, InvalidBufferLengthError> {
2258 match bytes {
2259 [b] => Ok(*b),
2260 bytes => Err(InvalidBufferLengthError(bytes.len())),
2261 }
2262}
2263
2264fn bytes_to_nonempty_str(bytes: &[u8]) -> Result<String, ProtocolError> {
2266 std::str::from_utf8(nonempty(bytes)?.into())
2270 .map_err(|e| ProtocolError::Utf8(e.valid_up_to()))
2271 .map(|string| string.trim_end_matches(ASCII_NULL).to_owned())
2272}
2273
2274fn bytes_to_addr(bytes: &[u8]) -> Result<Ipv4Addr, InvalidBufferLengthError> {
2276 Ok(Ipv4Addr::from(get_byte_array::<4>(bytes)?))
2277}
2278
2279fn bytes_to_addrs<const LOWER_BOUND: usize>(
2281 bytes: &[u8],
2282) -> Result<
2283 AtLeast<LOWER_BOUND, AtMostBytes<{ size_constrained::U8_MAX_AS_USIZE }, Vec<Ipv4Addr>>>,
2284 InvalidBufferLengthError,
2285> {
2286 let vec = bytes
2287 .chunks(IPV4_ADDR_LEN)
2288 .map(bytes_to_addr)
2289 .collect::<Result<Vec<Ipv4Addr>, InvalidBufferLengthError>>()
2290 .map_err(|InvalidBufferLengthError(_)| InvalidBufferLengthError(bytes.len()))?;
2291 vec.try_into().map_err(|(size_constrained::Error::SizeConstraintViolated, _)| {
2292 InvalidBufferLengthError(bytes.len())
2293 })
2294}
2295
2296#[derive(Debug, Error, PartialEq)]
2297enum BooleanConversionError {
2298 #[error("invalid buffer length: {}", _0)]
2299 InvalidBufferLength(usize),
2300 #[error("invalid value: {}", _0)]
2301 InvalidValue(u8),
2302}
2303
2304impl BooleanConversionError {
2305 fn to_protocol(&self, code: OptionCode) -> ProtocolError {
2306 match self {
2307 Self::InvalidBufferLength(len) => ProtocolError::InvalidBufferLength(*len),
2308 Self::InvalidValue(val) => ProtocolError::InvalidOptionValue(code, vec![*val]),
2309 }
2310 }
2311}
2312
2313fn bytes_to_bool(bytes: &[u8]) -> Result<bool, BooleanConversionError> {
2315 let byte = get_byte(bytes)?;
2316 match byte {
2317 0 | 1 => Ok(byte == 1),
2318 b => Err(BooleanConversionError::InvalidValue(b)),
2319 }
2320}
2321
2322pub fn ip_addr_from_buf_at(buf: &[u8], start: usize) -> Result<Ipv4Addr, ProtocolError> {
2324 let buf = buf.get(start..start + 4).ok_or(ProtocolError::InvalidBufferLength(buf.len()))?;
2325 let buf: [u8; 4] = buf.try_into().map_err(|std::array::TryFromSliceError { .. }| {
2326 ProtocolError::InvalidBufferLength(buf.len())
2327 })?;
2328 Ok(buf.into())
2329}
2330
2331const NULL_TERMINATOR: u8 = 0;
2332
2333fn buf_to_msg_string(buf: &[u8]) -> BString {
2334 let null_terminator_position =
2339 buf.iter().position(|&b| b == NULL_TERMINATOR).unwrap_or(buf.len());
2340 BString::from(&buf[..null_terminator_position])
2341}
2342fn trunc_string_to_n_and_push(s: &BString, n: usize, buffer: &mut Vec<u8>) {
2343 let s: &[u8] = s.as_ref();
2344 if s.len() >= n {
2349 let (truncated, _dropped) = s.split_at(n - 1);
2350 buffer.extend(truncated);
2351 buffer.push(NULL_TERMINATOR);
2352 return;
2353 }
2354 buffer.extend(s);
2355 let unused_bytes = n - s.len();
2356 let old_len = buffer.len();
2357 buffer.resize(old_len + unused_bytes, NULL_TERMINATOR);
2358}
2359
2360#[cfg(test)]
2361mod tests {
2362 use super::identifier::ClientIdentifier;
2363 use super::*;
2364 use net_declare::net::prefix_length_v4;
2365 use net_declare::std::ip_v4;
2366 use std::str::FromStr;
2367 use test_case::test_case;
2368
2369 const DEFAULT_SUBNET_MASK: PrefixLength<Ipv4> = prefix_length_v4!(24);
2370
2371 fn new_test_msg() -> Message {
2372 Message {
2373 op: OpCode::BOOTREQUEST,
2374 xid: 42,
2375 secs: 1024,
2376 bdcast_flag: false,
2377 ciaddr: Ipv4Addr::UNSPECIFIED,
2378 yiaddr: ip_v4!("192.168.1.1"),
2379 siaddr: Ipv4Addr::UNSPECIFIED,
2380 giaddr: Ipv4Addr::UNSPECIFIED,
2381 chaddr: MacAddr::new([0; 6]),
2382 sname: BString::from("relay.example.com"),
2383 file: BString::from("boot.img"),
2384 options: Vec::new(),
2385 }
2386 }
2387
2388 #[test]
2389 fn serialize_returns_correct_bytes() {
2390 let mut msg = new_test_msg();
2391 msg.options.push(DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK));
2392
2393 let bytes = msg.serialize();
2394
2395 assert_eq!(bytes.len(), 247);
2396 assert_eq!(bytes[0], 1u8);
2397 assert_eq!(bytes[1], 1u8);
2398 assert_eq!(bytes[2], 6u8);
2399 assert_eq!(bytes[3], 0u8);
2400 assert_eq!(bytes[7], 42u8);
2401 assert_eq!(bytes[8], 4u8);
2402 assert_eq!(bytes[16], 192u8);
2403 assert_eq!(bytes[17], 168u8);
2404 assert_eq!(bytes[18], 1u8);
2405 assert_eq!(bytes[19], 1u8);
2406 assert_eq!(bytes[44], 'r' as u8);
2407 assert_eq!(bytes[60], 'm' as u8);
2408 assert_eq!(bytes[61], 0u8);
2409 assert_eq!(bytes[108], 'b' as u8);
2410 assert_eq!(bytes[115], 'g' as u8);
2411 assert_eq!(bytes[116], 0u8);
2412 assert_eq!(bytes[OPTIONS_START_IDX..OPTIONS_START_IDX + MAGIC_COOKIE.len()], MAGIC_COOKIE);
2413 assert_eq!(bytes[bytes.len() - 1], 255u8);
2414 }
2415
2416 #[test]
2417 fn message_from_buffer_returns_correct_message() {
2418 let mut buf = Vec::new();
2419 buf.push(1u8);
2420 buf.push(1u8);
2421 buf.push(6u8);
2422 buf.push(0u8);
2423 buf.extend_from_slice(b"\x00\x00\x00\x2A");
2424 buf.extend_from_slice(b"\x04\x00");
2425 buf.extend_from_slice(b"\x00\x00");
2426 buf.extend_from_slice(b"\x00\x00\x00\x00");
2427 buf.extend_from_slice(b"\xC0\xA8\x01\x01");
2428 buf.extend_from_slice(b"\x00\x00\x00\x00");
2429 buf.extend_from_slice(b"\x00\x00\x00\x00");
2430 buf.extend_from_slice(b"\x00\x00\x00\x00\x00\x00");
2431 buf.extend_from_slice(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
2432 buf.extend_from_slice(b"relay.example.com");
2433 let mut old_len = buf.len();
2434 let mut unused_bytes = SNAME_LEN - b"relay.example.com".len();
2435 buf.resize(old_len + unused_bytes, 0u8);
2436 buf.extend_from_slice(b"boot.img");
2437 old_len = buf.len();
2438 unused_bytes = FILE_LEN - b"boot.img".len();
2439 buf.resize(old_len + unused_bytes, 0u8);
2440 buf.extend_from_slice(&MAGIC_COOKIE);
2441 buf.extend_from_slice(b"\x01\x04");
2442 buf.extend_from_slice(&DEFAULT_SUBNET_MASK.get_mask().ipv4_bytes()[..]);
2443 buf.extend_from_slice(b"\x00");
2444 buf.extend_from_slice(b"\x00");
2445 buf.extend_from_slice(b"\x36\x04");
2446 let server_id = ip_v4!("1.2.3.4");
2447 buf.extend_from_slice(&server_id.octets()[..]);
2448 buf.extend_from_slice(b"\xFF");
2449
2450 assert_eq!(
2451 Message::from_buffer(&buf),
2452 Ok(Message {
2453 op: OpCode::BOOTREQUEST,
2454 xid: 42,
2455 secs: 1024,
2456 bdcast_flag: false,
2457 ciaddr: Ipv4Addr::UNSPECIFIED,
2458 yiaddr: ip_v4!("192.168.1.1"),
2459 siaddr: Ipv4Addr::UNSPECIFIED,
2460 giaddr: Ipv4Addr::UNSPECIFIED,
2461 chaddr: MacAddr::new([0; 6]),
2462 sname: BString::from("relay.example.com"),
2463 file: BString::from("boot.img"),
2464 options: vec![
2465 DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK),
2466 DhcpOption::ServerIdentifier(server_id),
2467 ],
2468 })
2469 );
2470 }
2471
2472 #[test]
2473 fn serialize_then_deserialize_with_single_option_is_equal_to_starting_value() {
2474 let msg = || {
2475 let mut msg = new_test_msg();
2476 msg.options.push(DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK));
2477 msg
2478 };
2479
2480 assert_eq!(Message::from_buffer(&msg().serialize()), Ok(msg()));
2481 }
2482
2483 #[test]
2484 fn serialize_then_deserialize_with_no_options_is_equal_to_starting_value() {
2485 let msg = new_test_msg();
2486
2487 assert_eq!(Message::from_buffer(&msg.serialize()), Ok(new_test_msg()));
2488 }
2489
2490 #[test]
2491 fn serialize_then_deserialize_with_many_options_is_equal_to_starting_value() {
2492 let msg = || {
2493 let mut msg = new_test_msg();
2494 msg.options.push(DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK));
2495 msg.options.push(DhcpOption::NameServer([ip_v4!("1.2.3.4")].into()));
2496 msg.options.push(DhcpOption::DhcpMessageType(MessageType::DHCPDISCOVER));
2497 msg.options.push(DhcpOption::ParameterRequestList([OptionCode::SubnetMask].into()));
2498 msg.options.push(DhcpOption::PathMtuPlateauTable([1480u16].into()));
2499 msg
2500 };
2501
2502 assert_eq!(Message::from_buffer(&msg().serialize()), Ok(msg()));
2503 }
2504
2505 #[test]
2506 fn strips_null_from_option_strings() {
2507 let ascii_str = String::from("Hello World");
2508 let null_terminated_str = format!("{}{}", ascii_str, ASCII_NULL);
2509 let msg = Message {
2510 options: vec![DhcpOption::MeritDumpFile(null_terminated_str)],
2511 ..new_test_msg()
2512 };
2513 let Message { options: parsed_options, .. } = Message::from_buffer(&msg.serialize())
2514 .expect("Parsing serialized message should succeed.");
2515 assert_eq!(parsed_options, vec![DhcpOption::MeritDumpFile(ascii_str)]);
2516 }
2517
2518 #[test]
2519 fn message_from_too_short_buffer_returns_error() {
2520 let buf = vec![0u8, 0u8, 0u8];
2521
2522 assert_eq!(
2523 Message::from_buffer(&buf),
2524 Err(ProtocolError::InvalidBufferLength(buf.len()).into())
2525 );
2526 }
2527
2528 #[test]
2529 fn serialize_with_valid_option_returns_correct_bytes() {
2530 let opt = DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK);
2531 let mut bytes = Vec::with_capacity(6);
2532 opt.serialize_to(&mut bytes);
2533 assert_eq!(bytes.len(), 6);
2534 assert_eq!(bytes[0], 1);
2535 assert_eq!(bytes[1], 4);
2536 assert_eq!(bytes[2], 255);
2537 assert_eq!(bytes[3], 255);
2538 assert_eq!(bytes[4], 255);
2539 assert_eq!(bytes[5], 0);
2540 }
2541
2542 #[test]
2543 fn serialize_with_fixed_len_option_returns_correct_bytes() {
2544 let opt = DhcpOption::End();
2545 let mut bytes = Vec::with_capacity(1);
2546 opt.serialize_to(&mut bytes);
2547 assert_eq!(bytes.len(), 1);
2548 assert_eq!(bytes[0], 255);
2549 }
2550
2551 #[test]
2552 fn option_from_valid_buffer_has_correct_value() {
2553 let buf = vec![1, 4, 255, 255, 255, 0, 255];
2554 assert_eq!(
2555 parse_options(&buf[..], Vec::new()),
2556 Ok(vec![DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK)])
2557 );
2558 }
2559
2560 #[test]
2561 fn option_from_valid_buffer_with_fixed_length_returns_empty_options() {
2562 let buf = vec![255];
2563 assert_eq!(parse_options(&buf[..], Vec::new()), Ok(Vec::new()));
2564 }
2565
2566 #[test]
2567 fn option_from_valid_buffer_ignores_unknown_opcodes() {
2568 let buf = vec![254, 2, 1, 2, 255];
2569 assert_eq!(parse_options(&buf[..], Vec::new()), Ok(Vec::new()));
2570 }
2571
2572 #[test]
2573 fn option_stops_at_end_of_options() {
2574 let buf = vec![26, 2, 4, 0, 255, 26, 2, 4, 0];
2575 assert_eq!(parse_options(&buf[..], Vec::new()), Ok(vec![DhcpOption::InterfaceMtu(1024)]));
2576 }
2577
2578 #[test]
2579 fn option_from_buffer_with_invalid_length_returns_err() {
2580 let buf = vec![1, 6, 255, 255, 255, 0];
2581 assert_eq!(
2582 parse_options(&buf[..], Vec::new()),
2583 Err(ProtocolError::MalformedOption { code: 1, remaining: 4, want: 6 })
2584 );
2585 }
2586
2587 #[test]
2588 fn option_from_buffer_missing_length_returns_err() {
2589 let buf = vec![1];
2590 assert_eq!(
2591 parse_options(&buf[..], Vec::new()),
2592 Err(ProtocolError::MalformedOption { code: 1, remaining: 0, want: 1 })
2593 );
2594 }
2595
2596 #[test]
2597 fn option_from_buffer_missing_end_option_returns_err() {
2598 assert_eq!(
2599 parse_options(&[], Vec::new()),
2600 Err(ProtocolError::MissingOption(OptionCode::End))
2601 );
2602 }
2603
2604 #[test]
2605 fn get_dhcp_type_with_dhcp_type_option_returns_value() {
2606 let mut msg = new_test_msg();
2607 msg.options.push(DhcpOption::DhcpMessageType(MessageType::DHCPDISCOVER));
2608
2609 assert_eq!(msg.get_dhcp_type(), Ok(MessageType::DHCPDISCOVER));
2610 }
2611
2612 #[test]
2613 fn get_dhcp_type_without_dhcp_type_option_returns_err() {
2614 let msg = new_test_msg();
2615
2616 assert_eq!(
2617 msg.get_dhcp_type(),
2618 Err(ProtocolError::MissingOption(OptionCode::DhcpMessageType).into())
2619 );
2620 }
2621
2622 #[test]
2623 fn buf_into_options_with_invalid_option_parses_other_valid_options() {
2624 let msg = || {
2625 let mut msg = new_test_msg();
2626 msg.options.push(DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK));
2627 msg.options.push(DhcpOption::Router([ip_v4!("192.168.1.1")].into()));
2628 msg.options.push(DhcpOption::DhcpMessageType(MessageType::DHCPDISCOVER));
2629 msg
2630 };
2631
2632 let mut buf = msg().serialize();
2633 buf[OPTIONS_START_IDX + 4] = 99;
2635
2636 let mut expected_msg = msg();
2638 assert_eq!(expected_msg.options.remove(0), DhcpOption::SubnetMask(DEFAULT_SUBNET_MASK));
2639 assert_eq!(Message::from_buffer(&buf), Ok(expected_msg));
2640 }
2641
2642 #[test_case(OptionCode::HostName, 0; "Min length 1")]
2643 #[test_case(OptionCode::ClientIdentifier, 1; "Min length 2_1")]
2644 #[test_case(OptionCode::PathMtuPlateauTable, 1; "Min length 2_2")]
2645 #[test_case(OptionCode::Router, 0; "Min length 4")]
2646 #[test_case(OptionCode::PolicyFilter, 4; "Min length 8_1")]
2647 #[test_case(OptionCode::StaticRoute, 4; "Min length 8_2")]
2648 fn parse_options_with_invalid_min_lengths(code: OptionCode, len: usize) {
2649 let option = DhcpOption::from_raw_parts(code, &vec![0; len]);
2650 assert_eq!(Err(ProtocolError::InvalidBufferLength(len)), option)
2651 }
2652
2653 #[test_case(OptionCode::IpForwarding, 0; "Length = 1")]
2654 #[test_case(OptionCode::BootfileName, 0; "Length = 2")]
2655 #[test_case(OptionCode::TimeOffset, 0; "Length = 4")]
2656 fn parse_options_with_invalid_static_length(code: OptionCode, len: usize) {
2657 let option = DhcpOption::from_raw_parts(code, &vec![0; len]);
2658 assert_eq!(Err(ProtocolError::InvalidBufferLength(len)), option)
2659 }
2660
2661 #[test_case(OptionCode::PathMtuPlateauTable, 3; "Min length 2, multiple of 2")]
2662 #[test_case(OptionCode::MobileIpHomeAgent, 5; "Min length 0, multiple of 4")]
2663 #[test_case(OptionCode::PolicyFilter, 4; "PolicyFilter_4: Min length 8, multiple of 8")]
2664 #[test_case(OptionCode::PolicyFilter, 12; "PolicyFilter_12: Min length 8, multiple of 8 - 2")]
2665 #[test_case(OptionCode::StaticRoute, 4; "StaticRoute_4: Min length 8, multiple of 8 - 1")]
2666 #[test_case(OptionCode::StaticRoute, 12; "StaticRoute_12: Min length 8, multiple of 8 - 2")]
2667 fn parse_options_with_invalid_length_multiples(code: OptionCode, len: usize) {
2668 let option = DhcpOption::from_raw_parts(code, &vec![0; len]);
2669 assert_eq!(Err(ProtocolError::InvalidBufferLength(len)), option)
2670 }
2671
2672 #[test_case(OptionCode::IpForwarding)]
2673 #[test_case(OptionCode::NonLocalSourceRouting)]
2674 #[test_case(OptionCode::AllSubnetsLocal)]
2675 #[test_case(OptionCode::PerformMaskDiscovery)]
2676 #[test_case(OptionCode::MaskSupplier)]
2677 #[test_case(OptionCode::PerformRouterDiscovery)]
2678 #[test_case(OptionCode::TrailerEncapsulation)]
2679 #[test_case(OptionCode::EthernetEncapsulation)]
2680 #[test_case(OptionCode::TcpKeepaliveGarbage)]
2681 fn parse_options_with_invalid_flag_value(code: OptionCode) {
2682 let val = vec![2];
2683 let option = DhcpOption::from_raw_parts(code, &val);
2684 assert_eq!(Err(ProtocolError::InvalidOptionValue(code, val)), option)
2685 }
2686
2687 #[test_case(0)]
2688 #[test_case(4)]
2689 fn parse_options_with_invalid_overload_value(overload: u8) {
2690 let code = OptionCode::OptionOverload;
2691 let val = vec![overload];
2692 let option = DhcpOption::from_raw_parts(code, &val);
2693
2694 assert_eq!(Err(ProtocolError::InvalidOptionValue(code, val)), option);
2696 }
2697
2698 #[test]
2699 fn parse_options_with_invalid_netbios_node_value() {
2700 let code = OptionCode::NetBiosOverTcpipNodeType;
2702 let val = vec![3];
2703 let option = DhcpOption::from_raw_parts(code, &val);
2704 assert_eq!(Err(ProtocolError::InvalidOptionValue(code, val)), option);
2705 }
2706
2707 #[test_case(OptionCode::DefaultIpTtl)]
2708 #[test_case(OptionCode::TcpDefaultTtl)]
2709 fn parse_options_with_invalid_ttl_value(code: OptionCode) {
2710 let val = vec![0];
2711 let option = DhcpOption::from_raw_parts(code, &val);
2712 assert_eq!(Err(ProtocolError::InvalidOptionValue(code, val)), option);
2713 }
2714
2715 #[test_case(OptionCode::MaxDatagramReassemblySize, MIN_MESSAGE_SIZE)]
2716 #[test_case(OptionCode::MaxDhcpMessageSize, MIN_MESSAGE_SIZE)]
2717 #[test_case(OptionCode::InterfaceMtu, MIN_MTU_VAL)]
2718 fn parse_options_with_too_low_value(code: OptionCode, min_size: u16) {
2719 let val = (min_size - 1).to_be_bytes().to_vec();
2720 let option = DhcpOption::from_raw_parts(code, &val);
2721 assert_eq!(Err(ProtocolError::InvalidOptionValue(code, val)), option);
2722 }
2723
2724 #[test]
2725 fn rejects_invalid_subnet_mask() {
2726 let mask = vec![255, 254, 255, 0];
2727
2728 assert_eq!(
2729 DhcpOption::from_raw_parts(OptionCode::SubnetMask, &mask),
2730 Err(ProtocolError::InvalidOptionValue(OptionCode::SubnetMask, mask))
2731 )
2732 }
2733
2734 #[test]
2735 fn parameter_request_list_with_known_and_unknown_options_returns_known_options() {
2736 assert_eq!(
2737 DhcpOption::from_raw_parts(
2738 OptionCode::ParameterRequestList,
2739 &[
2740 121, 1, 3, 6, 15, 31, 33, 249, 43, 44, 46, 47, 119, 252, ]
2745 ),
2746 Ok(DhcpOption::ParameterRequestList(
2747 [
2748 OptionCode::SubnetMask,
2749 OptionCode::Router,
2750 OptionCode::DomainNameServer,
2751 OptionCode::DomainName,
2752 OptionCode::PerformRouterDiscovery,
2753 OptionCode::StaticRoute,
2754 OptionCode::VendorSpecificInformation,
2755 OptionCode::NetBiosOverTcpipNameServer,
2756 OptionCode::NetBiosOverTcpipNodeType,
2757 OptionCode::NetBiosOverTcpipScope,
2758 ]
2759 .into()
2760 ))
2761 );
2762 }
2763
2764 fn random_ipv4_generator() -> Ipv4Addr {
2765 let (octet1, octet2, octet3, octet4) = rand::random();
2766 Ipv4Addr::new(octet1, octet2, octet3, octet4)
2767 }
2768
2769 fn test_option_overload(overload: Overload) {
2770 let mut msg = Message {
2771 op: OpCode::BOOTREQUEST,
2772 xid: 0,
2773 secs: 0,
2774 bdcast_flag: false,
2775 ciaddr: Ipv4Addr::UNSPECIFIED,
2776 yiaddr: Ipv4Addr::UNSPECIFIED,
2777 siaddr: Ipv4Addr::UNSPECIFIED,
2778 giaddr: Ipv4Addr::UNSPECIFIED,
2779 chaddr: MacAddr::new([0; 6]),
2780 sname: BString::default(),
2781 file: BString::default(),
2782 options: vec![DhcpOption::OptionOverload(overload)],
2783 }
2784 .serialize();
2785 let ip = random_ipv4_generator();
2786 let first_extra_opt = {
2787 let mut acc = Vec::new();
2788 DhcpOption::RequestedIpAddress(ip).serialize_to(&mut acc);
2789 acc
2790 };
2791 let last_extra_opt = {
2792 let mut acc = Vec::new();
2793 DhcpOption::End().serialize_to(&mut acc);
2794 acc
2795 };
2796 let (extra_opts, start_idx) = match overload {
2797 Overload::SName => ([&first_extra_opt[..], &last_extra_opt[..]].concat(), SNAME_IDX),
2798 Overload::File => ([&first_extra_opt[..], &last_extra_opt[..]].concat(), FILE_IDX),
2799 Overload::Both => {
2800 ([&first_extra_opt[..], &[0u8; SNAME_LEN], &last_extra_opt[..]].concat(), SNAME_IDX)
2803 }
2804 };
2805 let _: std::vec::Splice<'_, _> =
2806 msg.splice(start_idx..start_idx + extra_opts.len(), extra_opts);
2807 assert_eq!(
2808 Message::from_buffer(&msg),
2809 Ok(Message {
2810 op: OpCode::BOOTREQUEST,
2811 xid: 0,
2812 secs: 0,
2813 bdcast_flag: false,
2814 ciaddr: Ipv4Addr::UNSPECIFIED,
2815 yiaddr: Ipv4Addr::UNSPECIFIED,
2816 siaddr: Ipv4Addr::UNSPECIFIED,
2817 giaddr: Ipv4Addr::UNSPECIFIED,
2818 chaddr: MacAddr::new([0; 6]),
2819 sname: BString::default(),
2820 file: BString::default(),
2821 options: vec![
2822 DhcpOption::OptionOverload(overload),
2823 DhcpOption::RequestedIpAddress(ip)
2824 ],
2825 })
2826 );
2827 }
2828
2829 #[test]
2830 fn message_with_option_overload_parses_extra_options() {
2831 test_option_overload(Overload::SName);
2832 test_option_overload(Overload::File);
2833 test_option_overload(Overload::Both);
2834 }
2835
2836 #[test]
2837 fn client_identifier_from_str() {
2838 assert_matches::assert_matches!(
2839 ClientIdentifier::from_str("id:1234567890abcd"),
2840 Ok(ClientIdentifier { .. })
2841 );
2842 assert_matches::assert_matches!(
2843 ClientIdentifier::from_str("chaddr:1234567890ab"),
2844 Ok(ClientIdentifier { .. })
2845 );
2846 assert_matches::assert_matches!(ClientIdentifier::from_str("option:1234567890"), Err(..));
2848 assert_matches::assert_matches!(ClientIdentifier::from_str("id:1234567890:extra"), Err(..));
2850 assert_matches::assert_matches!(ClientIdentifier::from_str("1234567890"), Err(..));
2852 assert_matches::assert_matches!(ClientIdentifier::from_str("id1234567890"), Err(..));
2854 assert_matches::assert_matches!(ClientIdentifier::from_str("id-1234567890"), Err(..));
2856 assert_matches::assert_matches!(
2858 ClientIdentifier::from_str("id:1234567890abcdefg"),
2859 Err(..)
2860 );
2861 assert_matches::assert_matches!(ClientIdentifier::from_str("id:123456789"), Err(..));
2863 assert_matches::assert_matches!(ClientIdentifier::from_str("chaddr:1234567890"), Err(..));
2865 }
2866
2867 #[test]
2868 fn buf_to_msg_string_truncates_at_null_terminator() {
2869 let buf = [b'a', b'b', b'c', 0, b'd', b'e', b'f'];
2870 assert_eq!(buf_to_msg_string(&buf), BString::from("abc"));
2871 }
2872
2873 #[test]
2874 fn trunc_string_to_n_and_push_adds_null_terminator() {
2875 let string = BString::from("abc");
2876
2877 let mut buf = Vec::new();
2879 trunc_string_to_n_and_push(&string, 4, &mut buf);
2880 assert_eq!(&buf[..], &[b'a', b'b', b'c', 0,]);
2881
2882 let mut buf = Vec::new();
2884 trunc_string_to_n_and_push(&string, 3, &mut buf);
2885 assert_eq!(&buf[..], &[b'a', b'b', 0]);
2886 }
2887}