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