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