1use mdns::protocol::{Domain, ParseError as MdnsParseError};
8use net_types::ip::{IpAddress as _, Ipv6Addr, PrefixTooLongError, Subnet};
9use num_derive::FromPrimitive;
10use packet::records::{
11 ParsedRecord, RecordBuilder, RecordParseResult, RecordSequenceBuilder, Records, RecordsImpl,
12 RecordsImplLayout,
13};
14use packet::{BufferView, BufferViewMut, InnerPacketBuilder, ParsablePacket, ParseMetadata};
15use std::convert::Infallible as Never;
16use std::slice::Iter;
17use std::{mem, str};
18use thiserror::Error;
19use uuid::Uuid;
20use zerocopy::byteorder::network_endian::{U16, U32};
21use zerocopy::{
22 FromBytes, Immutable, IntoByteSlice, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned,
23};
24
25#[allow(missing_docs)]
27#[derive(Debug, Error, PartialEq)]
28pub enum ParseError {
29 #[error("invalid message type: {}", _0)]
30 InvalidMessageType(u8),
31 #[error("invalid option code: {}", _0)]
32 InvalidOpCode(u16),
33 #[error("invalid option length {} for option code {:?}", _1, _0)]
34 InvalidOpLen(OptionCode, usize),
35 #[error("invalid status code: {}", _0)]
36 InvalidStatusCode(u16),
37 #[error("invalid error status code: {}", _0)]
38 InvalidErrorStatusCode(u16),
39 #[error("buffer exhausted while more bytes are expected")]
40 BufferExhausted,
41 #[error("failed to parse domain {:?}", _0)]
42 DomainParseError(MdnsParseError),
43 #[error("failed to parse UTF8 string: {:?}", _0)]
44 Utf8Error(#[from] str::Utf8Error),
45}
46
47impl From<Never> for ParseError {
48 fn from(err: Never) -> ParseError {
49 match err {}
50 }
51}
52
53#[allow(missing_docs)]
57#[derive(Debug, PartialEq, FromPrimitive, IntoBytes, Immutable, Copy, Clone)]
58#[repr(u8)]
59pub enum MessageType {
60 Solicit = 1,
61 Advertise = 2,
62 Request = 3,
63 Confirm = 4,
64 Renew = 5,
65 Rebind = 6,
66 Reply = 7,
67 Release = 8,
68 Decline = 9,
69 Reconfigure = 10,
70 InformationRequest = 11,
71 RelayForw = 12,
72 RelayRepl = 13,
73}
74
75impl From<MessageType> for u8 {
76 fn from(t: MessageType) -> u8 {
77 t as u8
78 }
79}
80
81impl TryFrom<u8> for MessageType {
82 type Error = ParseError;
83
84 fn try_from(b: u8) -> Result<MessageType, ParseError> {
85 <Self as num_traits::FromPrimitive>::from_u8(b).ok_or(ParseError::InvalidMessageType(b))
86 }
87}
88
89#[allow(missing_docs)]
93#[derive(Debug, PartialEq, Copy, Clone)]
94pub enum StatusCode {
95 Success,
96 Failure(ErrorStatusCode),
97}
98
99impl From<StatusCode> for u16 {
100 fn from(t: StatusCode) -> u16 {
101 match t {
102 StatusCode::Success => 0,
103 StatusCode::Failure(error_status) => error_status.into(),
104 }
105 }
106}
107
108impl TryFrom<u16> for StatusCode {
109 type Error = ParseError;
110
111 fn try_from(b: u16) -> Result<StatusCode, ParseError> {
112 match b {
113 0 => Ok(Self::Success),
114 b => ErrorStatusCode::try_from(b).map(Self::Failure).map_err(|e| match e {
115 ParseError::InvalidErrorStatusCode(b) => ParseError::InvalidStatusCode(b),
116 e => unreachable!("unexpected error parsing u16 as ErrorStatusCode: {}", e),
117 }),
118 }
119 }
120}
121
122impl StatusCode {
123 pub fn into_result(self) -> Result<(), ErrorStatusCode> {
126 match self {
127 Self::Success => Ok(()),
128 Self::Failure(error_status) => Err(error_status),
129 }
130 }
131}
132
133#[allow(missing_docs)]
137#[derive(thiserror::Error, Debug, PartialEq, FromPrimitive, IntoBytes, Immutable, Copy, Clone)]
138#[repr(u16)]
139pub enum ErrorStatusCode {
140 #[error("unspecified failure")]
141 UnspecFail = 1,
142 #[error("no addresses available")]
143 NoAddrsAvail = 2,
144 #[error("no binding")]
145 NoBinding = 3,
146 #[error("not on-link")]
147 NotOnLink = 4,
148 #[error("use multicast")]
149 UseMulticast = 5,
150 #[error("no prefixes available")]
151 NoPrefixAvail = 6,
152}
153
154impl From<ErrorStatusCode> for u16 {
155 fn from(code: ErrorStatusCode) -> u16 {
156 code as u16
157 }
158}
159
160impl From<ErrorStatusCode> for StatusCode {
161 fn from(code: ErrorStatusCode) -> Self {
162 Self::Failure(code)
163 }
164}
165
166impl TryFrom<u16> for ErrorStatusCode {
167 type Error = ParseError;
168
169 fn try_from(b: u16) -> Result<Self, ParseError> {
170 <Self as num_traits::FromPrimitive>::from_u16(b)
171 .ok_or(ParseError::InvalidErrorStatusCode(b))
172 }
173}
174
175#[allow(missing_docs)]
182#[derive(Debug, PartialEq, FromPrimitive, Clone, Copy)]
183#[repr(u8)]
184pub enum OptionCode {
185 ClientId = 1,
186 ServerId = 2,
187 Iana = 3,
188 IaAddr = 5,
189 Oro = 6,
190 Preference = 7,
191 ElapsedTime = 8,
192 StatusCode = 13,
193 DnsServers = 23,
194 DomainList = 24,
195 IaPd = 25,
196 IaPrefix = 26,
197 InformationRefreshTime = 32,
198 SolMaxRt = 82,
199}
200
201impl From<OptionCode> for u16 {
202 fn from(code: OptionCode) -> u16 {
203 code as u16
204 }
205}
206
207impl TryFrom<u16> for OptionCode {
208 type Error = ParseError;
209
210 fn try_from(n: u16) -> Result<OptionCode, ParseError> {
211 <Self as num_traits::FromPrimitive>::from_u16(n).ok_or(ParseError::InvalidOpCode(n))
212 }
213}
214
215#[allow(missing_docs)]
223#[derive(Debug, PartialEq)]
224pub enum ParsedDhcpOption<'a> {
225 ClientId(&'a Duid),
227 ServerId(&'a Duid),
229 Iana(IanaData<&'a [u8]>),
233 IaAddr(IaAddrData<&'a [u8]>),
237 Oro(Vec<OptionCode>),
242 Preference(u8),
244 ElapsedTime(u16),
246 StatusCode(U16, &'a str),
248 IaPd(IaPdData<&'a [u8]>),
252 IaPrefix(IaPrefixData<&'a [u8]>),
256 InformationRefreshTime(u32),
258 SolMaxRt(U32),
260 DnsServers(Vec<Ipv6Addr>),
262 DomainList(Vec<checked::Domain>),
264}
265
266#[derive(Debug, PartialEq)]
268pub struct IanaData<B: SplitByteSlice> {
269 header: Ref<B, IanaHeader>,
270 options: Records<B, ParsedDhcpOptionImpl>,
271}
272
273mod private {
274 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
276 pub struct NonZeroOrMaxU32(u32);
277
278 impl NonZeroOrMaxU32 {
279 pub const fn new(t: u32) -> Option<NonZeroOrMaxU32> {
283 if t == 0 || t == u32::MAX {
284 return None;
285 }
286 Some(NonZeroOrMaxU32(t))
287 }
288
289 pub fn get(self) -> u32 {
291 let NonZeroOrMaxU32(t) = self;
292 t
293 }
294 }
295}
296
297pub use private::*;
298
299#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
307pub enum NonZeroTimeValue {
308 Finite(NonZeroOrMaxU32),
310 Infinity,
315}
316
317impl From<NonZeroTimeValue> for TimeValue {
318 fn from(v: NonZeroTimeValue) -> TimeValue {
319 TimeValue::NonZero(v)
320 }
321}
322
323#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
330pub enum TimeValue {
331 Zero,
333 NonZero(NonZeroTimeValue),
335}
336
337impl TimeValue {
338 pub const fn new(t: u32) -> TimeValue {
340 match t {
341 0 => TimeValue::Zero,
342 u32::MAX => TimeValue::NonZero(NonZeroTimeValue::Infinity),
343 t => TimeValue::NonZero(NonZeroTimeValue::Finite(
344 NonZeroOrMaxU32::new(t).unwrap(),
346 )),
347 }
348 }
349}
350
351impl<'a, B: SplitByteSlice> IanaData<B> {
352 fn new(buf: B) -> Result<Self, ParseError> {
354 let buf_len = buf.len();
355 let (header, options) =
356 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
357 ParseError::InvalidOpLen(OptionCode::Iana, buf_len)
358 })?;
359 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, ())?;
360 Ok(IanaData { header, options })
361 }
362
363 pub fn iaid(&self) -> u32 {
365 self.header.iaid.get()
366 }
367
368 pub fn t1(&self) -> TimeValue {
375 TimeValue::new(self.header.t1.get())
376 }
377
378 pub fn t2(&self) -> TimeValue {
385 TimeValue::new(self.header.t2.get())
386 }
387
388 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
390 self.options.iter()
391 }
392}
393
394#[derive(
396 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
397)]
398#[repr(C)]
399struct IanaHeader {
400 iaid: U32,
401 t1: U32,
402 t2: U32,
403}
404
405#[derive(Debug, PartialEq)]
407pub struct IaAddrData<B: SplitByteSlice> {
408 header: Ref<B, IaAddrHeader>,
409 options: Records<B, ParsedDhcpOptionImpl>,
410}
411
412impl<'a, B: SplitByteSlice> IaAddrData<B> {
413 pub fn new(buf: B) -> Result<Self, ParseError> {
415 let buf_len = buf.len();
416 let (header, options) =
417 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
418 ParseError::InvalidOpLen(OptionCode::IaAddr, buf_len)
419 })?;
420 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, ())?;
421 Ok(IaAddrData { header, options })
422 }
423
424 pub fn addr(&self) -> Ipv6Addr {
426 self.header.addr
427 }
428
429 pub fn preferred_lifetime(&self) -> TimeValue {
436 TimeValue::new(self.header.preferred_lifetime.get())
437 }
438
439 pub fn valid_lifetime(&self) -> TimeValue {
446 TimeValue::new(self.header.valid_lifetime.get())
447 }
448
449 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
451 self.options.iter()
452 }
453}
454
455#[derive(
457 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
458)]
459#[repr(C)]
460struct IaAddrHeader {
461 addr: Ipv6Addr,
462 preferred_lifetime: U32,
463 valid_lifetime: U32,
464}
465
466#[derive(
468 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
469)]
470#[repr(C)]
471struct IaPdHeader {
472 iaid: U32,
473 t1: U32,
474 t2: U32,
475}
476
477#[derive(Debug, PartialEq)]
481pub struct IaPdData<B: SplitByteSlice> {
482 header: Ref<B, IaPdHeader>,
483 options: Records<B, ParsedDhcpOptionImpl>,
484}
485
486impl<'a, B: SplitByteSlice> IaPdData<B> {
487 fn new(buf: B) -> Result<Self, ParseError> {
489 let buf_len = buf.len();
490 let (header, options) =
491 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
492 ParseError::InvalidOpLen(OptionCode::IaPd, buf_len)
493 })?;
494 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, ())?;
495 Ok(IaPdData { header, options })
496 }
497
498 pub fn iaid(&self) -> u32 {
500 self.header.iaid.get()
501 }
502
503 pub fn t1(&self) -> TimeValue {
510 TimeValue::new(self.header.t1.get())
511 }
512
513 pub fn t2(&self) -> TimeValue {
520 TimeValue::new(self.header.t2.get())
521 }
522
523 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
525 self.options.iter()
526 }
527}
528
529#[derive(
531 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
532)]
533#[repr(C)]
534struct IaPrefixHeader {
535 preferred_lifetime_secs: U32,
536 valid_lifetime_secs: U32,
537 prefix_length: u8,
538 prefix: Ipv6Addr,
539}
540
541#[derive(Debug, PartialEq)]
545pub struct IaPrefixData<B: SplitByteSlice> {
546 header: Ref<B, IaPrefixHeader>,
547 options: Records<B, ParsedDhcpOptionImpl>,
548}
549
550impl<'a, B: SplitByteSlice> IaPrefixData<B> {
551 pub fn new(buf: B) -> Result<Self, ParseError> {
553 let buf_len = buf.len();
554 let (header, options) =
555 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
556 ParseError::InvalidOpLen(OptionCode::IaPrefix, buf_len)
557 })?;
558 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, ())?;
559 Ok(IaPrefixData { header, options })
560 }
561
562 pub fn prefix(&self) -> Result<Subnet<Ipv6Addr>, PrefixTooLongError> {
564 Subnet::from_host(self.header.prefix, self.header.prefix_length)
565 }
566
567 pub fn preferred_lifetime(&self) -> TimeValue {
574 TimeValue::new(self.header.preferred_lifetime_secs.get())
575 }
576
577 pub fn valid_lifetime(&self) -> TimeValue {
584 TimeValue::new(self.header.valid_lifetime_secs.get())
585 }
586
587 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
589 self.options.iter()
590 }
591}
592
593mod checked {
594 use std::str::FromStr;
595
596 use mdns::protocol::{DomainBuilder, EmbeddedPacketBuilder};
597 use packet::BufferViewMut;
598 use zerocopy::SplitByteSliceMut;
599
600 use super::ParseError;
601
602 #[derive(Debug, PartialEq)]
604 pub struct Domain {
605 domain: String,
606 builder: DomainBuilder,
607 }
608
609 impl FromStr for Domain {
610 type Err = ParseError;
611
612 fn from_str(s: &str) -> Result<Self, ParseError> {
616 Self::try_from(s.to_string())
617 }
618 }
619
620 impl TryFrom<String> for Domain {
621 type Error = ParseError;
622
623 fn try_from(domain: String) -> Result<Self, ParseError> {
631 let builder = DomainBuilder::from_str(&domain).map_err(ParseError::DomainParseError)?;
632 Ok(Domain { domain, builder })
633 }
634 }
635
636 impl Domain {
637 pub(crate) fn bytes_len(&self) -> usize {
638 self.builder.bytes_len()
639 }
640
641 pub(crate) fn serialize<B: SplitByteSliceMut, BV: BufferViewMut<B>>(&self, bv: &mut BV) {
642 let () = self.builder.serialize(bv);
643 }
644 }
645}
646
647macro_rules! option_to_code {
648 ($option:ident, $($option_name:ident::$variant:tt($($v:tt)*)),*) => {
649 match $option {
650 $($option_name::$variant($($v)*)=>OptionCode::$variant,)*
651 }
652 }
653}
654
655impl ParsedDhcpOption<'_> {
656 pub fn code(&self) -> OptionCode {
658 option_to_code!(
659 self,
660 ParsedDhcpOption::ClientId(_),
661 ParsedDhcpOption::ServerId(_),
662 ParsedDhcpOption::Iana(_),
663 ParsedDhcpOption::IaAddr(_),
664 ParsedDhcpOption::Oro(_),
665 ParsedDhcpOption::Preference(_),
666 ParsedDhcpOption::ElapsedTime(_),
667 ParsedDhcpOption::StatusCode(_, _),
668 ParsedDhcpOption::IaPd(_),
669 ParsedDhcpOption::IaPrefix(_),
670 ParsedDhcpOption::InformationRefreshTime(_),
671 ParsedDhcpOption::SolMaxRt(_),
672 ParsedDhcpOption::DnsServers(_),
673 ParsedDhcpOption::DomainList(_)
674 )
675 }
676}
677
678type Duid = [u8];
682
683#[derive(Debug, PartialEq)]
690enum ParsedDhcpOptionImpl {}
691
692impl RecordsImplLayout for ParsedDhcpOptionImpl {
693 type Context = ();
694
695 type Error = ParseError;
696}
697
698impl RecordsImpl for ParsedDhcpOptionImpl {
699 type Record<'a> = ParsedDhcpOption<'a>;
700
701 fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
707 data: &mut BV,
708 _context: &mut Self::Context,
709 ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
710 if data.len() == 0 {
711 return Ok(ParsedRecord::Done);
712 }
713
714 let opt_code = data.take_obj_front::<U16>().ok_or(ParseError::BufferExhausted)?;
715 let opt_len = data.take_obj_front::<U16>().ok_or(ParseError::BufferExhausted)?;
716 let opt_len = usize::from(opt_len.get());
717 let mut opt_val = data.take_front(opt_len).ok_or(ParseError::BufferExhausted)?;
718
719 let opt_code = match OptionCode::try_from(opt_code.get()) {
720 Ok(opt_code) => opt_code,
721 Err(ParseError::InvalidOpCode(_)) => {
724 return Ok(ParsedRecord::Skipped);
728 }
729 Err(e) => unreachable!("unexpected error from op code conversion: {}", e),
730 };
731
732 let opt = match opt_code {
733 OptionCode::ClientId => Ok(ParsedDhcpOption::ClientId(opt_val)),
734 OptionCode::ServerId => Ok(ParsedDhcpOption::ServerId(opt_val)),
735 OptionCode::Iana => IanaData::new(opt_val).map(ParsedDhcpOption::Iana),
736 OptionCode::IaAddr => IaAddrData::new(opt_val).map(ParsedDhcpOption::IaAddr),
737 OptionCode::Oro => {
738 let options = opt_val
739 .chunks(2)
741 .map(|opt| {
742 let opt: [u8; 2] = opt.try_into().map_err(
743 |std::array::TryFromSliceError { .. }| {
744 ParseError::InvalidOpLen(OptionCode::Oro, opt_val.len())
745 },
746 )?;
747 OptionCode::try_from(u16::from_be_bytes(opt))
748 })
749 .collect::<Result<_, ParseError>>()?;
750 Ok(ParsedDhcpOption::Oro(options))
751 }
752 OptionCode::Preference => match opt_val {
753 &[b] => Ok(ParsedDhcpOption::Preference(b)),
754 opt_val => Err(ParseError::InvalidOpLen(OptionCode::Preference, opt_val.len())),
755 },
756 OptionCode::ElapsedTime => match opt_val {
757 &[b0, b1] => Ok(ParsedDhcpOption::ElapsedTime(u16::from_be_bytes([b0, b1]))),
758 opt_val => Err(ParseError::InvalidOpLen(OptionCode::ElapsedTime, opt_val.len())),
759 },
760 OptionCode::StatusCode => {
761 let mut opt_val = &mut opt_val;
762 let code = (&mut opt_val).take_obj_front::<U16>().ok_or_else(|| {
763 ParseError::InvalidOpLen(OptionCode::StatusCode, opt_val.len())
764 })?;
765 let message = str::from_utf8(opt_val)?;
766 Ok(ParsedDhcpOption::StatusCode(*code, message))
767 }
768 OptionCode::IaPd => IaPdData::new(opt_val).map(ParsedDhcpOption::IaPd),
769 OptionCode::IaPrefix => IaPrefixData::new(opt_val).map(ParsedDhcpOption::IaPrefix),
770 OptionCode::InformationRefreshTime => match opt_val {
771 &[b0, b1, b2, b3] => {
772 Ok(ParsedDhcpOption::InformationRefreshTime(u32::from_be_bytes([
773 b0, b1, b2, b3,
774 ])))
775 }
776 opt_val => {
777 Err(ParseError::InvalidOpLen(OptionCode::InformationRefreshTime, opt_val.len()))
778 }
779 },
780 OptionCode::SolMaxRt => {
781 let mut opt_val = &mut opt_val;
782 let sol_max_rt = (&mut opt_val)
783 .take_obj_front::<U32>()
784 .ok_or_else(|| ParseError::InvalidOpLen(OptionCode::SolMaxRt, opt_val.len()))?;
785 Ok(ParsedDhcpOption::SolMaxRt(*sol_max_rt))
786 }
787 OptionCode::DnsServers => {
788 let addresses = opt_val
789 .chunks(16)
791 .map(|opt| {
792 let opt: [u8; 16] = opt.try_into().map_err(
793 |std::array::TryFromSliceError { .. }| {
794 ParseError::InvalidOpLen(OptionCode::DnsServers, opt_val.len())
795 },
796 )?;
797 Ok(Ipv6Addr::from(opt))
798 })
799 .collect::<Result<_, ParseError>>()?;
800 Ok(ParsedDhcpOption::DnsServers(addresses))
801 }
802 OptionCode::DomainList => {
803 let mut opt_val = &mut opt_val;
804 let mut domains = Vec::new();
805 while opt_val.len() > 0 {
806 domains.push(checked::Domain::try_from(
807 Domain::parse(
808 &mut opt_val,
809 None,
815 )
816 .map_err(ParseError::DomainParseError)?
817 .to_string(),
818 )?);
819 }
820 Ok(ParsedDhcpOption::DomainList(domains))
821 }
822 }?;
823
824 Ok(ParsedRecord::Parsed(opt))
825 }
826}
827
828pub fn duid_uuid() -> [u8; 18] {
832 let mut duid = [0u8; 18];
833 duid[1] = 4;
834 let uuid = Uuid::new_v4();
835 let uuid = uuid.as_bytes();
836 duid[2..].copy_from_slice(&uuid[..]);
837 duid
838}
839
840#[allow(missing_docs)]
848#[derive(Debug)]
849pub enum DhcpOption<'a> {
850 ClientId(&'a Duid),
852 ServerId(&'a Duid),
854 Iana(IanaSerializer<'a>),
858 IaAddr(IaAddrSerializer<'a>),
862 Oro(&'a [OptionCode]),
867 Preference(u8),
869 ElapsedTime(u16),
871 StatusCode(u16, &'a str),
873 IaPd(IaPdSerializer<'a>),
875 IaPrefix(IaPrefixSerializer<'a>),
877 InformationRefreshTime(u32),
879 SolMaxRt(u32),
881 DnsServers(&'a [Ipv6Addr]),
883 DomainList(&'a [checked::Domain]),
885}
886
887#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
891pub struct IAID(u32);
892
893impl IAID {
894 pub const fn new(iaid: u32) -> Self {
896 Self(iaid)
897 }
898
899 pub fn get(&self) -> u32 {
901 let IAID(iaid) = self;
902 *iaid
903 }
904}
905
906#[derive(Debug)]
908pub struct IanaSerializer<'a> {
909 header: IanaHeader,
910 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
911}
912
913impl<'a> IanaSerializer<'a> {
914 pub fn new(iaid: IAID, t1: u32, t2: u32, options: &'a [DhcpOption<'a>]) -> IanaSerializer<'a> {
916 IanaSerializer {
917 header: IanaHeader { iaid: U32::new(iaid.get()), t1: U32::new(t1), t2: U32::new(t2) },
918 options: RecordSequenceBuilder::new(options.iter()),
919 }
920 }
921}
922
923#[derive(Debug)]
925pub struct IaAddrSerializer<'a> {
926 header: IaAddrHeader,
927 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
928}
929
930impl<'a> IaAddrSerializer<'a> {
931 pub fn new(
933 addr: Ipv6Addr,
934 preferred_lifetime: u32,
935 valid_lifetime: u32,
936 options: &'a [DhcpOption<'a>],
937 ) -> IaAddrSerializer<'a> {
938 IaAddrSerializer {
939 header: IaAddrHeader {
940 addr,
941 preferred_lifetime: U32::new(preferred_lifetime),
942 valid_lifetime: U32::new(valid_lifetime),
943 },
944 options: RecordSequenceBuilder::new(options.iter()),
945 }
946 }
947}
948
949#[derive(Debug)]
951pub struct IaPdSerializer<'a> {
952 header: IaPdHeader,
953 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
954}
955
956impl<'a> IaPdSerializer<'a> {
957 pub fn new(iaid: IAID, t1: u32, t2: u32, options: &'a [DhcpOption<'a>]) -> IaPdSerializer<'a> {
959 IaPdSerializer {
960 header: IaPdHeader { iaid: U32::new(iaid.get()), t1: U32::new(t1), t2: U32::new(t2) },
961 options: RecordSequenceBuilder::new(options.iter()),
962 }
963 }
964}
965
966#[derive(Debug)]
968pub struct IaPrefixSerializer<'a> {
969 header: IaPrefixHeader,
970 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
971}
972
973impl<'a> IaPrefixSerializer<'a> {
974 pub fn new(
976 preferred_lifetime_secs: u32,
977 valid_lifetime_secs: u32,
978 prefix: Subnet<Ipv6Addr>,
979 options: &'a [DhcpOption<'a>],
980 ) -> IaPrefixSerializer<'a> {
981 IaPrefixSerializer {
982 header: IaPrefixHeader {
983 preferred_lifetime_secs: U32::new(preferred_lifetime_secs),
984 valid_lifetime_secs: U32::new(valid_lifetime_secs),
985 prefix_length: prefix.prefix(),
986 prefix: prefix.network(),
987 },
988 options: RecordSequenceBuilder::new(options.iter()),
989 }
990 }
991}
992
993impl DhcpOption<'_> {
994 pub fn code(&self) -> OptionCode {
996 option_to_code!(
997 self,
998 DhcpOption::ClientId(_),
999 DhcpOption::ServerId(_),
1000 DhcpOption::Iana(_),
1001 DhcpOption::IaAddr(_),
1002 DhcpOption::Oro(_),
1003 DhcpOption::Preference(_),
1004 DhcpOption::ElapsedTime(_),
1005 DhcpOption::StatusCode(_, _),
1006 DhcpOption::IaPd(_),
1007 DhcpOption::IaPrefix(_),
1008 DhcpOption::InformationRefreshTime(_),
1009 DhcpOption::SolMaxRt(_),
1010 DhcpOption::DnsServers(_),
1011 DhcpOption::DomainList(_)
1012 )
1013 }
1014}
1015
1016impl<'a> RecordBuilder for DhcpOption<'a> {
1017 fn serialized_len(&self) -> usize {
1025 4 + match self {
1026 DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => {
1027 u16::try_from(duid.len()).unwrap_or(18).into()
1028 }
1029 DhcpOption::Iana(IanaSerializer { header, options }) => {
1030 u16::try_from(header.as_bytes().len() + options.serialized_len())
1031 .expect("overflows")
1032 .into()
1033 }
1034 DhcpOption::IaAddr(IaAddrSerializer { header, options }) => {
1035 u16::try_from(header.as_bytes().len() + options.serialized_len())
1036 .expect("overflows")
1037 .into()
1038 }
1039 DhcpOption::Oro(opts) => u16::try_from(2 * opts.len()).unwrap_or(0).into(),
1040 DhcpOption::Preference(v) => std::mem::size_of_val(v),
1041 DhcpOption::ElapsedTime(v) => std::mem::size_of_val(v),
1042 DhcpOption::StatusCode(v, message) => std::mem::size_of_val(v) + message.len(),
1043 DhcpOption::IaPd(IaPdSerializer { header, options }) => {
1044 u16::try_from(header.as_bytes().len() + options.serialized_len())
1045 .expect("overflows")
1046 .into()
1047 }
1048 DhcpOption::IaPrefix(IaPrefixSerializer { header, options }) => {
1049 u16::try_from(header.as_bytes().len() + options.serialized_len())
1050 .expect("overflows")
1051 .into()
1052 }
1053 DhcpOption::InformationRefreshTime(v) => std::mem::size_of_val(v),
1054 DhcpOption::SolMaxRt(v) => std::mem::size_of_val(v),
1055 DhcpOption::DnsServers(recursive_name_servers) => {
1056 u16::try_from(16 * recursive_name_servers.len()).unwrap_or(0).into()
1057 }
1058 DhcpOption::DomainList(domains) => {
1059 u16::try_from(domains.iter().fold(0, |tot, domain| tot + domain.bytes_len()))
1060 .unwrap_or(0)
1061 .into()
1062 }
1063 }
1064 }
1065
1066 fn serialize_into(&self, mut buf: &mut [u8]) {
1081 let mut buf = &mut buf;
1083 let () = buf.write_obj_front(&U16::new(self.code().into())).expect("buffer is too small");
1084
1085 match self {
1086 DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => {
1087 match u16::try_from(duid.len()) {
1088 Ok(len) => {
1089 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1090 let () = buf.write_obj_front(*duid).expect("buffer is too small");
1091 }
1092 Err(std::num::TryFromIntError { .. }) => {
1093 let duid = duid_uuid();
1096 let len = u16::try_from(duid.len()).expect("uuid length is too long");
1097 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1098 let () = buf.write_obj_front(&duid).expect("buffer is too small");
1099 }
1100 }
1101 }
1102 DhcpOption::Iana(IanaSerializer { header, options }) => {
1103 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1104 .expect("overflows");
1105 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1106 let () = buf.write_obj_front(header).expect("buffer is too small");
1107 let () = options.serialize_into(buf);
1108 }
1109 DhcpOption::IaAddr(IaAddrSerializer { header, options }) => {
1110 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1111 .expect("overflows");
1112 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1113 let () = buf.write_obj_front(header).expect("buffer is too small");
1114 let () = options.serialize_into(buf);
1115 }
1116 DhcpOption::Oro(requested_opts) => {
1117 let (requested_opts, len) = u16::try_from(2 * requested_opts.len()).map_or_else(
1118 |std::num::TryFromIntError { .. }| {
1119 (&[][..], 0)
1122 },
1123 |len| (*requested_opts, len),
1124 );
1125 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1126 for opt_code in requested_opts.into_iter() {
1127 let () = buf
1128 .write_obj_front(&u16::from(*opt_code).to_be_bytes())
1129 .expect("buffer is too small");
1130 }
1131 }
1132 DhcpOption::Preference(pref_val) => {
1133 let () = buf.write_obj_front(&U16::new(1)).expect("buffer is too small");
1134 let () = buf.write_obj_front(pref_val).expect("buffer is too small");
1135 }
1136 DhcpOption::ElapsedTime(elapsed_time) => {
1137 let () = buf
1138 .write_obj_front(&U16::new(
1139 mem::size_of_val(elapsed_time).try_into().expect("overflows"),
1140 ))
1141 .expect("buffer is too small");
1142 let () =
1143 buf.write_obj_front(&U16::new(*elapsed_time)).expect("buffer is too small");
1144 }
1145 DhcpOption::StatusCode(code, message) => {
1146 let opt_len = u16::try_from(2 + message.len()).expect("overflows");
1147 let () = buf.write_obj_front(&U16::new(opt_len)).expect("buffer is too small");
1148 let () = buf.write_obj_front(&U16::new(*code)).expect("buffer is too small");
1149 let () = buf.write_obj_front(message.as_bytes()).expect("buffer is too small");
1150 }
1151 DhcpOption::IaPd(IaPdSerializer { header, options }) => {
1152 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1153 .expect("overflows");
1154 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1155 buf.write_obj_front(header).expect("buffer is too small");
1156 let () = options.serialize_into(buf);
1157 }
1158 DhcpOption::IaPrefix(IaPrefixSerializer { header, options }) => {
1159 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1160 .expect("overflows");
1161 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1162 buf.write_obj_front(header).expect("buffer is too small");
1163 let () = options.serialize_into(buf);
1164 }
1165 DhcpOption::InformationRefreshTime(information_refresh_time) => {
1166 let () = buf
1167 .write_obj_front(&U16::new(
1168 mem::size_of_val(information_refresh_time).try_into().expect("overflows"),
1169 ))
1170 .expect("buffer is too small");
1171 let () = buf
1172 .write_obj_front(&U32::new(*information_refresh_time))
1173 .expect("buffer is too small");
1174 }
1175 DhcpOption::SolMaxRt(sol_max_rt) => {
1176 let () = buf
1177 .write_obj_front(&U16::new(
1178 mem::size_of_val(sol_max_rt).try_into().expect("overflows"),
1179 ))
1180 .expect("buffer is too small");
1181 let () = buf.write_obj_front(&U32::new(*sol_max_rt)).expect("buffer is too small");
1182 }
1183 DhcpOption::DnsServers(recursive_name_servers) => {
1184 let (recursive_name_servers, len) =
1185 u16::try_from(16 * recursive_name_servers.len()).map_or_else(
1186 |std::num::TryFromIntError { .. }| {
1187 (&[][..], 0)
1190 },
1191 |len| (*recursive_name_servers, len),
1192 );
1193 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1194 recursive_name_servers.iter().for_each(|server_addr| {
1195 let () = buf.write_obj_front(server_addr.bytes()).expect("buffer is too small");
1196 })
1197 }
1198 DhcpOption::DomainList(domains) => {
1199 let (domains, len) =
1200 u16::try_from(domains.iter().map(|domain| domain.bytes_len()).sum::<usize>())
1201 .map_or_else(
1202 |std::num::TryFromIntError { .. }| {
1203 (&[][..], 0)
1206 },
1207 |len| (*domains, len),
1208 );
1209 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1210 domains.iter().for_each(|domain| {
1211 domain.serialize(&mut buf);
1212 })
1213 }
1214 }
1215 }
1216}
1217
1218type TransactionId = [u8; 3];
1222
1223#[derive(Debug)]
1227pub struct Message<'a, B> {
1228 msg_type: MessageType,
1229 transaction_id: &'a TransactionId,
1230 options: Records<B, ParsedDhcpOptionImpl>,
1231}
1232
1233impl<'a, B: SplitByteSlice> Message<'a, B> {
1234 pub fn msg_type(&self) -> MessageType {
1236 self.msg_type
1237 }
1238
1239 pub fn transaction_id(&self) -> &TransactionId {
1241 &self.transaction_id
1242 }
1243
1244 pub fn options<'b: 'a>(&'b self) -> impl 'b + Iterator<Item = ParsedDhcpOption<'a>> {
1246 self.options.iter()
1247 }
1248}
1249
1250impl<'a, B: 'a + SplitByteSlice + IntoByteSlice<'a>> ParsablePacket<B, ()> for Message<'a, B> {
1251 type Error = ParseError;
1252
1253 fn parse_metadata(&self) -> ParseMetadata {
1254 let Self { msg_type, transaction_id, options } = self;
1255 ParseMetadata::from_packet(
1256 0,
1257 mem::size_of_val(msg_type) + mem::size_of_val(transaction_id) + options.bytes().len(),
1258 0,
1259 )
1260 }
1261
1262 fn parse<BV: BufferView<B>>(mut buf: BV, _args: ()) -> Result<Self, ParseError> {
1263 let msg_type =
1264 MessageType::try_from(buf.take_byte_front().ok_or(ParseError::BufferExhausted)?)?;
1265 let transaction_id = Ref::into_ref(
1266 buf.take_obj_front::<TransactionId>().ok_or(ParseError::BufferExhausted)?,
1267 );
1268 let options = Records::<_, ParsedDhcpOptionImpl>::parse(buf.take_rest_front())?;
1269 Ok(Message { msg_type, transaction_id, options })
1270 }
1271}
1272
1273#[derive(Debug)]
1280pub struct MessageBuilder<'a> {
1281 msg_type: MessageType,
1282 transaction_id: TransactionId,
1283 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
1284}
1285
1286impl<'a> MessageBuilder<'a> {
1287 pub fn new(
1289 msg_type: MessageType,
1290 transaction_id: TransactionId,
1291 options: &'a [DhcpOption<'a>],
1292 ) -> MessageBuilder<'a> {
1293 MessageBuilder {
1294 msg_type,
1295 transaction_id,
1296 options: RecordSequenceBuilder::new(options.iter()),
1297 }
1298 }
1299}
1300
1301impl InnerPacketBuilder for MessageBuilder<'_> {
1302 fn bytes_len(&self) -> usize {
1307 let Self { msg_type, transaction_id, options } = self;
1308 mem::size_of_val(msg_type) + mem::size_of_val(transaction_id) + options.serialized_len()
1309 }
1310
1311 fn serialize(&self, mut buffer: &mut [u8]) {
1319 let Self { msg_type, transaction_id, options } = self;
1320 let mut buffer = &mut buffer;
1322 let () = buffer.write_obj_front(msg_type).expect("buffer is too small");
1323 let () = buffer.write_obj_front(transaction_id).expect("buffer is too small");
1324 let () = options.serialize_into(buffer);
1325 }
1326}
1327
1328#[cfg(test)]
1329mod tests {
1330 use super::*;
1331 use assert_matches::assert_matches;
1332 use net_declare::{net_ip_v6, net_subnet_v6};
1333 use std::str::FromStr;
1334 use test_case::test_case;
1335
1336 fn test_buf_with_no_options() -> Vec<u8> {
1337 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &[]);
1338 let mut buf = vec![0; builder.bytes_len()];
1339 let () = builder.serialize(&mut buf);
1340 buf
1341 }
1342
1343 #[test]
1344 fn test_message_serialization() {
1345 let iaaddr_options = [DhcpOption::StatusCode(0, "Success.")];
1346 let iana_options = [
1347 DhcpOption::Preference(42),
1348 DhcpOption::IaAddr(IaAddrSerializer::new(
1349 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1350 3600,
1351 7200,
1352 &iaaddr_options,
1353 )),
1354 ];
1355 let iaprefix_options = [DhcpOption::StatusCode(0, "Success.")];
1356 let iapd_options = [DhcpOption::IaPrefix(IaPrefixSerializer::new(
1357 9999,
1358 6666,
1359 net_subnet_v6!("abcd:1234::/56"),
1360 &iaprefix_options,
1361 ))];
1362 let dns_servers = [
1363 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1364 Ipv6Addr::from([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]),
1365 ];
1366 let domains = [
1367 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1368 checked::Domain::from_str("www.google.com").expect("failed to construct test domain"),
1369 ];
1370 let options = [
1371 DhcpOption::ClientId(&[4, 5, 6]),
1372 DhcpOption::ServerId(&[8]),
1373 DhcpOption::Iana(IanaSerializer::new(IAID::new(42), 3000, 6500, &iana_options)),
1374 DhcpOption::Oro(&[OptionCode::ClientId, OptionCode::ServerId]),
1375 DhcpOption::Preference(42),
1376 DhcpOption::ElapsedTime(3600),
1377 DhcpOption::StatusCode(0, "Success."),
1378 DhcpOption::IaPd(IaPdSerializer::new(IAID::new(44), 5000, 7000, &iapd_options)),
1379 DhcpOption::InformationRefreshTime(86400),
1380 DhcpOption::SolMaxRt(86400),
1381 DhcpOption::DnsServers(&dns_servers),
1382 DhcpOption::DomainList(&domains),
1383 ];
1384 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1385 assert_eq!(builder.bytes_len(), 256);
1386 let mut buf = vec![0; builder.bytes_len()];
1387 let () = builder.serialize(&mut buf);
1388
1389 #[rustfmt::skip]
1390 assert_eq!(
1391 buf[..],
1392 [
1393 1, 1, 2, 3, 0, 1, 0, 3, 4, 5, 6, 0, 2, 0, 1, 8, 0, 3, 0, 59, 0, 0, 0, 42, 0, 0, 11, 184, 0, 0, 25, 100, 0, 7, 0, 1, 42, 0, 5, 0, 38, 0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215, 0, 0, 14, 16, 0, 0, 28, 32, 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1399 0, 6, 0, 4, 0, 1, 0, 2, 0, 7, 0, 1, 42, 0, 8, 0, 2, 14, 16, 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1404
1405 0, 25, 0, 55,
1407 0, 0, 0, 44,
1409 0, 0, 19, 136,
1411 0, 0, 27, 88,
1413 0, 26, 0, 39,
1416 0, 0, 39, 15,
1418 0, 0, 26, 10,
1420 56,
1422 0xab, 0xcd, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00,
1424 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1425 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1428
1429 0, 32, 0, 4, 0, 1, 81, 128, 0, 82, 0, 4, 0, 1, 81, 128, 0, 23, 0, 32,
1433 0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215,
1434 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
1435 0, 24, 0, 29,
1437 7, 102, 117, 99, 104, 115, 105, 97, 3, 100, 101, 118, 0,
1438 3, 119, 119, 119, 6, 103, 111, 111, 103, 108, 101, 3, 99, 111, 109, 0
1439 ],
1440 );
1441 }
1442
1443 #[test]
1444 fn test_message_serialization_parsing_roundtrip() {
1445 let iaaddr_suboptions = [DhcpOption::StatusCode(0, "Success.")];
1446 let iana_suboptions = [
1447 DhcpOption::Preference(42),
1448 DhcpOption::IaAddr(IaAddrSerializer::new(
1449 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1450 7200,
1451 9000,
1452 &iaaddr_suboptions,
1453 )),
1454 ];
1455 let iaprefix_options = [DhcpOption::StatusCode(0, "Success.")];
1456 let iapd_options = [DhcpOption::IaPrefix(IaPrefixSerializer::new(
1457 89658902,
1458 82346231,
1459 net_subnet_v6!("1234:5678:1231::/48"),
1460 &iaprefix_options,
1461 ))];
1462 let dns_servers = [net_ip_v6!("::")];
1463 let domains = [
1464 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1465 checked::Domain::from_str("www.google.com").expect("failed to construct test domain"),
1466 ];
1467 let options = [
1468 DhcpOption::ClientId(&[4, 5, 6]),
1469 DhcpOption::ServerId(&[8]),
1470 DhcpOption::Iana(IanaSerializer::new(IAID::new(1234), 7000, 8800, &iana_suboptions)),
1471 DhcpOption::Oro(&[OptionCode::ClientId, OptionCode::ServerId]),
1472 DhcpOption::Preference(42),
1473 DhcpOption::ElapsedTime(3600),
1474 DhcpOption::StatusCode(0, "Success."),
1475 DhcpOption::IaPd(IaPdSerializer::new(IAID::new(1412), 6513, 9876, &iapd_options)),
1476 DhcpOption::InformationRefreshTime(86400),
1477 DhcpOption::SolMaxRt(86400),
1478 DhcpOption::DnsServers(&dns_servers),
1479 DhcpOption::DomainList(&domains),
1480 ];
1481 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1482 let mut buf = vec![0; builder.bytes_len()];
1483 let () = builder.serialize(&mut buf);
1484
1485 let mut buf = &buf[..];
1486 let msg = Message::parse(&mut buf, ()).expect("parse should succeed");
1487 assert_eq!(msg.msg_type, MessageType::Solicit);
1488 assert_eq!(msg.transaction_id, &[1, 2, 3]);
1489 let got_options: Vec<_> = msg.options.iter().collect();
1490
1491 let iana_buf = [
1492 0, 0, 4, 210, 0, 0, 27, 88, 0, 0, 34, 96, 0, 7, 0, 1, 42, 0, 5, 0, 38, 0, 1, 2, 3, 4,
1493 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215, 0, 0, 28, 32, 0, 0, 35, 40, 0, 13,
1494 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1495 ];
1496 let iapd_buf = [
1497 0, 0, 5, 132, 0, 0, 25, 113, 0, 0, 38, 148, 0, 26, 0, 39, 5, 88, 22, 22, 4, 232, 128, 247, 48, 0x12, 0x34, 0x56, 0x78, 0x12, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1507 0x00, 0x00, 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1509 ];
1510 let options = [
1511 ParsedDhcpOption::ClientId(&[4, 5, 6]),
1512 ParsedDhcpOption::ServerId(&[8]),
1513 ParsedDhcpOption::Iana(IanaData::new(&iana_buf[..]).expect("construction failed")),
1514 ParsedDhcpOption::Oro(vec![OptionCode::ClientId, OptionCode::ServerId]),
1515 ParsedDhcpOption::Preference(42),
1516 ParsedDhcpOption::ElapsedTime(3600),
1517 ParsedDhcpOption::StatusCode(U16::new(0), "Success."),
1518 ParsedDhcpOption::IaPd(
1519 IaPdData::new(&iapd_buf[..]).expect("IA_PD construction failed"),
1520 ),
1521 ParsedDhcpOption::InformationRefreshTime(86400),
1522 ParsedDhcpOption::SolMaxRt(U32::new(86400)),
1523 ParsedDhcpOption::DnsServers(vec![Ipv6Addr::from([0; 16])]),
1524 ParsedDhcpOption::DomainList(vec![
1525 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1526 checked::Domain::from_str("www.google.com")
1527 .expect("failed to construct test domain"),
1528 ]),
1529 ];
1530 assert_eq!(got_options, options);
1531 }
1532
1533 const OVERFLOW_LENGTH: usize = u16::MAX as usize + 1;
1535
1536 #[test]
1537 fn test_message_serialization_duid_too_long() {
1538 let options = [DhcpOption::ClientId(&[0u8; OVERFLOW_LENGTH])];
1539 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1540 let mut buf = vec![0; builder.bytes_len()];
1541 let () = builder.serialize(&mut buf);
1542
1543 assert_eq!(buf.len(), 26);
1544 assert_eq!(
1545 buf[..8],
1546 [
1547 1, 1, 2, 3, 0, 1, 0, 18, ],
1551 );
1552
1553 let mut buf = &buf[..];
1555 let _: Message<'_, _> = Message::parse(&mut buf, ()).expect("parse should succeed");
1556 }
1557
1558 #[test]
1559 fn test_message_serialization_oro_too_long() {
1560 let options = [DhcpOption::Oro(&[OptionCode::Preference; OVERFLOW_LENGTH][..])];
1561 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1562 let mut buf = vec![0; builder.bytes_len()];
1563 let () = builder.serialize(&mut buf);
1564
1565 assert_eq!(
1566 buf[..],
1567 [
1568 1, 1, 2, 3, 0, 6, 0, 0, ],
1572 );
1573
1574 let mut buf = &buf[..];
1576 let _: Message<'_, _> = Message::parse(&mut buf, ()).expect("parse should succeed");
1577 }
1578
1579 #[test]
1580 fn test_option_serialization_parsing_roundtrip() {
1581 let mut buf = [0u8; 6];
1582 let option = DhcpOption::ElapsedTime(42);
1583
1584 option.serialize_into(&mut buf);
1585 assert_eq!(buf, [0, 8, 0, 2, 0, 42]);
1586
1587 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(&buf[..], ())
1588 .expect("parse should succeed");
1589 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1590 assert_eq!(options[..], [ParsedDhcpOption::ElapsedTime(42)]);
1591 }
1592
1593 #[test]
1594 fn test_buffer_too_short() {
1595 let buf = [];
1596 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1597
1598 let buf = [
1599 1, 0, ];
1602 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1603
1604 let buf = [
1605 1, 1, 2, 3, 0, ];
1609 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1610
1611 let buf = [
1612 1, 1, 2, 3, 0, 1, 0, ];
1617 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1618
1619 let buf = [
1621 1, 1, 2, 3, 0, 1, 0, 100, 1, 2, ];
1627 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1628 }
1629
1630 #[test]
1631 fn test_invalid_message_type() {
1632 let mut buf = test_buf_with_no_options();
1633 buf[0] = 0;
1635 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::InvalidMessageType(0)));
1636 }
1637
1638 #[test]
1639 fn test_skip_invalid_op_code() {
1640 let mut buf = test_buf_with_no_options();
1641 buf.extend_from_slice(&[
1642 0, 0, 0, 1, 0, 0, 1, 0, 3, 4, 5, 6, ]);
1647 let mut buf = &buf[..];
1648 let msg = Message::parse(&mut buf, ()).expect("parse should succeed");
1649 let got_options: Vec<_> = msg.options.iter().collect();
1650 assert_eq!(got_options, [ParsedDhcpOption::ClientId(&[4, 5, 6])]);
1651 }
1652
1653 #[test]
1654 fn test_iana_no_suboptions_serialization_parsing_roundtrip() {
1655 let mut buf = [0u8; 16];
1656 let option = DhcpOption::Iana(IanaSerializer::new(IAID::new(3456), 1024, 54321, &[]));
1657
1658 option.serialize_into(&mut buf);
1659 assert_eq!(buf, [0, 3, 0, 12, 0, 0, 13, 128, 0, 0, 4, 0, 0, 0, 212, 49]);
1660
1661 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(&buf[..], ())
1662 .expect("parse should succeed");
1663 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1664 let iana_buf = [0, 0, 13, 128, 0, 0, 4, 0, 0, 0, 212, 49];
1665 assert_eq!(
1666 options[..],
1667 [ParsedDhcpOption::Iana(IanaData::new(&iana_buf[..]).expect("construction failed"))]
1668 );
1669 }
1670
1671 #[test]
1675 fn test_iana_invalid_opt_len() {
1676 let mut buf = test_buf_with_no_options();
1677 buf.extend_from_slice(&[
1678 0, 3, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1681 ]);
1682 assert_matches!(
1683 Message::parse(&mut &buf[..], ()),
1684 Err(ParseError::InvalidOpLen(OptionCode::Iana, 8))
1685 );
1686 }
1687
1688 #[test]
1689 fn test_iaaddr_no_suboptions_serialization_parsing_roundtrip() {
1690 let mut buf = [0u8; 28];
1691 let option = DhcpOption::IaAddr(IaAddrSerializer::new(
1692 Ipv6Addr::from([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]),
1693 0,
1694 0,
1695 &[],
1696 ));
1697
1698 option.serialize_into(&mut buf);
1699 assert_eq!(
1700 buf,
1701 [
1702 0, 5, 0, 24, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0,
1703 0, 0, 0, 0, 0, 0
1704 ]
1705 );
1706
1707 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(&buf[..], ())
1708 .expect("parse should succeed");
1709 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1710 let iaaddr_buf = [
1711 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 0, 0,
1712 ];
1713 assert_eq!(
1714 options[..],
1715 [ParsedDhcpOption::IaAddr(
1716 IaAddrData::new(&iaaddr_buf[..]).expect("construction failed")
1717 )]
1718 );
1719 }
1720
1721 #[test]
1725 fn test_iaaddr_invalid_opt_len() {
1726 let mut buf = test_buf_with_no_options();
1727 buf.extend_from_slice(&[
1728 0, 5, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1731 ]);
1732 assert_matches!(
1733 Message::parse(&mut &buf[..], ()),
1734 Err(ParseError::InvalidOpLen(OptionCode::IaAddr, 8))
1735 );
1736 }
1737
1738 #[test]
1742 fn test_invalid_oro_opt_len() {
1743 let mut buf = test_buf_with_no_options();
1744 buf.extend_from_slice(&[
1745 0, 6, 0, 1, 0,
1748 ]);
1749 assert_matches!(
1750 Message::parse(&mut &buf[..], ()),
1751 Err(ParseError::InvalidOpLen(OptionCode::Oro, 1))
1752 );
1753 }
1754
1755 #[test]
1759 fn test_invalid_preference_opt_len() {
1760 let mut buf = test_buf_with_no_options();
1761 buf.extend_from_slice(&[
1762 0, 7, 0, 2, 0, 0,
1765 ]);
1766 assert_matches!(
1767 Message::parse(&mut &buf[..], ()),
1768 Err(ParseError::InvalidOpLen(OptionCode::Preference, 2))
1769 );
1770 }
1771
1772 #[test]
1776 fn test_elapsed_time_invalid_opt_len() {
1777 let mut buf = test_buf_with_no_options();
1778 buf.extend_from_slice(&[
1779 0, 8, 0, 3, 0, 0, 0,
1782 ]);
1783 assert_matches!(
1784 Message::parse(&mut &buf[..], ()),
1785 Err(ParseError::InvalidOpLen(OptionCode::ElapsedTime, 3))
1786 );
1787 }
1788
1789 #[test]
1793 fn test_status_code_invalid_opt_len() {
1794 let mut buf = test_buf_with_no_options();
1795 buf.extend_from_slice(&[
1796 0, 13, 0, 1, 0, 0, 0,
1799 ]);
1800 assert_matches!(
1801 Message::parse(&mut &buf[..], ()),
1802 Err(ParseError::InvalidOpLen(OptionCode::StatusCode, 1))
1803 );
1804 }
1805 #[test]
1809 fn test_information_refresh_time_invalid_opt_len() {
1810 let mut buf = test_buf_with_no_options();
1811 buf.extend_from_slice(&[
1812 0, 32, 0, 3, 0, 0, 0,
1815 ]);
1816 assert_matches!(
1817 Message::parse(&mut &buf[..], ()),
1818 Err(ParseError::InvalidOpLen(OptionCode::InformationRefreshTime, 3))
1819 );
1820 }
1821
1822 #[test]
1826 fn test_sol_max_rt_invalid_opt_len() {
1827 let mut buf = test_buf_with_no_options();
1828 buf.extend_from_slice(&[
1829 0, 82, 0, 3, 0, 0, 0,
1832 ]);
1833 assert_matches!(
1834 Message::parse(&mut &buf[..], ()),
1835 Err(ParseError::InvalidOpLen(OptionCode::SolMaxRt, 3))
1836 );
1837 }
1838 #[test]
1842 fn test_dns_servers_invalid_opt_len() {
1843 let mut buf = test_buf_with_no_options();
1844 buf.extend_from_slice(&[
1845 0, 23, 0, 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1848 ]);
1849 assert_matches!(
1850 Message::parse(&mut &buf[..], ()),
1851 Err(ParseError::InvalidOpLen(OptionCode::DnsServers, 17))
1852 );
1853 }
1854
1855 #[test_case(TimeValue::new(0), TimeValue::Zero)]
1856 #[test_case(TimeValue::new(5), TimeValue::NonZero(NonZeroTimeValue::Finite(NonZeroOrMaxU32::new(5).expect("should succeed for non zero or u32::MAX values"))))]
1857 #[test_case(TimeValue::new(u32::MAX), TimeValue::NonZero(NonZeroTimeValue::Infinity))]
1858 fn test_time_value_new(time_value: TimeValue, expected_variant: TimeValue) {
1859 assert_eq!(time_value, expected_variant);
1860 }
1861
1862 #[test_case(
1863 NonZeroTimeValue::Finite(
1864 NonZeroOrMaxU32::new(1)
1865 .expect("should succeed for non zero or u32::MAX values")
1866 ))]
1867 #[test_case(NonZeroTimeValue::Infinity)]
1868 fn test_time_value_ord(non_zero_tv: NonZeroTimeValue) {
1869 assert!(TimeValue::Zero < TimeValue::NonZero(non_zero_tv));
1870 }
1871
1872 #[test]
1873 fn test_non_zero_time_value_ord() {
1874 assert!(
1875 NonZeroTimeValue::Finite(
1876 NonZeroOrMaxU32::new(u32::MAX - 1)
1877 .expect("should succeed for non zero or u32::MAX values")
1878 ) < NonZeroTimeValue::Infinity
1879 );
1880 }
1881
1882 #[test_case(0, None)]
1883 #[test_case(60, Some(NonZeroOrMaxU32::new(60).unwrap()))]
1884 #[test_case(u32::MAX, None)]
1885 fn test_non_zero_or_max_u32_new(t: u32, expected: Option<NonZeroOrMaxU32>) {
1886 assert_eq!(NonZeroOrMaxU32::new(t), expected);
1887 }
1888
1889 #[test_case(1)]
1890 #[test_case(4321)]
1891 #[test_case(u32::MAX - 1)]
1892 fn test_non_zero_or_max_u32_get(t: u32) {
1893 assert_eq!(
1894 NonZeroOrMaxU32::new(t).expect("should succeed for non-zero or u32::MAX values").get(),
1895 t
1896 );
1897 }
1898}