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, RecordsContext,
12 RecordsImpl, 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 #[error("DHCPv6 option recursion limit exceeded")]
46 OptionRecursionLimitExceeded,
47}
48
49impl From<Never> for ParseError {
50 fn from(err: Never) -> ParseError {
51 match err {}
52 }
53}
54
55const MAX_RECURSION_DEPTH: usize = 4;
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66struct OptionParseContext {
67 depth: usize,
68}
69
70impl RecordsContext for OptionParseContext {}
71
72#[allow(missing_docs)]
76#[derive(Debug, PartialEq, FromPrimitive, IntoBytes, Immutable, Copy, Clone)]
77#[repr(u8)]
78pub enum MessageType {
79 Solicit = 1,
80 Advertise = 2,
81 Request = 3,
82 Confirm = 4,
83 Renew = 5,
84 Rebind = 6,
85 Reply = 7,
86 Release = 8,
87 Decline = 9,
88 Reconfigure = 10,
89 InformationRequest = 11,
90 RelayForw = 12,
91 RelayRepl = 13,
92}
93
94impl From<MessageType> for u8 {
95 fn from(t: MessageType) -> u8 {
96 t as u8
97 }
98}
99
100impl TryFrom<u8> for MessageType {
101 type Error = ParseError;
102
103 fn try_from(b: u8) -> Result<MessageType, ParseError> {
104 <Self as num_traits::FromPrimitive>::from_u8(b).ok_or(ParseError::InvalidMessageType(b))
105 }
106}
107
108#[allow(missing_docs)]
112#[derive(Debug, PartialEq, Copy, Clone)]
113pub enum StatusCode {
114 Success,
115 Failure(ErrorStatusCode),
116}
117
118impl From<StatusCode> for u16 {
119 fn from(t: StatusCode) -> u16 {
120 match t {
121 StatusCode::Success => 0,
122 StatusCode::Failure(error_status) => error_status.into(),
123 }
124 }
125}
126
127impl TryFrom<u16> for StatusCode {
128 type Error = ParseError;
129
130 fn try_from(b: u16) -> Result<StatusCode, ParseError> {
131 match b {
132 0 => Ok(Self::Success),
133 b => ErrorStatusCode::try_from(b).map(Self::Failure).map_err(|e| match e {
134 ParseError::InvalidErrorStatusCode(b) => ParseError::InvalidStatusCode(b),
135 e => unreachable!("unexpected error parsing u16 as ErrorStatusCode: {}", e),
136 }),
137 }
138 }
139}
140
141impl StatusCode {
142 pub fn into_result(self) -> Result<(), ErrorStatusCode> {
145 match self {
146 Self::Success => Ok(()),
147 Self::Failure(error_status) => Err(error_status),
148 }
149 }
150}
151
152#[allow(missing_docs)]
156#[derive(thiserror::Error, Debug, PartialEq, FromPrimitive, IntoBytes, Immutable, Copy, Clone)]
157#[repr(u16)]
158pub enum ErrorStatusCode {
159 #[error("unspecified failure")]
160 UnspecFail = 1,
161 #[error("no addresses available")]
162 NoAddrsAvail = 2,
163 #[error("no binding")]
164 NoBinding = 3,
165 #[error("not on-link")]
166 NotOnLink = 4,
167 #[error("use multicast")]
168 UseMulticast = 5,
169 #[error("no prefixes available")]
170 NoPrefixAvail = 6,
171}
172
173impl From<ErrorStatusCode> for u16 {
174 fn from(code: ErrorStatusCode) -> u16 {
175 code as u16
176 }
177}
178
179impl From<ErrorStatusCode> for StatusCode {
180 fn from(code: ErrorStatusCode) -> Self {
181 Self::Failure(code)
182 }
183}
184
185impl TryFrom<u16> for ErrorStatusCode {
186 type Error = ParseError;
187
188 fn try_from(b: u16) -> Result<Self, ParseError> {
189 <Self as num_traits::FromPrimitive>::from_u16(b)
190 .ok_or(ParseError::InvalidErrorStatusCode(b))
191 }
192}
193
194#[allow(missing_docs)]
201#[derive(Debug, PartialEq, FromPrimitive, Clone, Copy)]
202#[repr(u8)]
203pub enum OptionCode {
204 ClientId = 1,
205 ServerId = 2,
206 Iana = 3,
207 IaAddr = 5,
208 Oro = 6,
209 Preference = 7,
210 ElapsedTime = 8,
211 StatusCode = 13,
212 DnsServers = 23,
213 DomainList = 24,
214 IaPd = 25,
215 IaPrefix = 26,
216 InformationRefreshTime = 32,
217 SolMaxRt = 82,
218}
219
220impl From<OptionCode> for u16 {
221 fn from(code: OptionCode) -> u16 {
222 code as u16
223 }
224}
225
226impl TryFrom<u16> for OptionCode {
227 type Error = ParseError;
228
229 fn try_from(n: u16) -> Result<OptionCode, ParseError> {
230 <Self as num_traits::FromPrimitive>::from_u16(n).ok_or(ParseError::InvalidOpCode(n))
231 }
232}
233
234#[allow(missing_docs)]
242#[derive(Debug, PartialEq)]
243pub enum ParsedDhcpOption<'a> {
244 ClientId(&'a Duid),
246 ServerId(&'a Duid),
248 Iana(IanaData<&'a [u8]>),
252 IaAddr(IaAddrData<&'a [u8]>),
256 Oro(Vec<OptionCode>),
261 Preference(u8),
263 ElapsedTime(u16),
265 StatusCode(U16, &'a str),
267 IaPd(IaPdData<&'a [u8]>),
271 IaPrefix(IaPrefixData<&'a [u8]>),
275 InformationRefreshTime(u32),
277 SolMaxRt(U32),
279 DnsServers(Vec<Ipv6Addr>),
281 DomainList(Vec<checked::Domain>),
283}
284
285#[derive(Debug, PartialEq)]
287pub struct IanaData<B: SplitByteSlice> {
288 header: Ref<B, IanaHeader>,
289 options: Records<B, ParsedDhcpOptionImpl>,
290}
291
292mod private {
293 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
295 pub struct NonZeroOrMaxU32(u32);
296
297 impl NonZeroOrMaxU32 {
298 pub const fn new(t: u32) -> Option<NonZeroOrMaxU32> {
302 if t == 0 || t == u32::MAX {
303 return None;
304 }
305 Some(NonZeroOrMaxU32(t))
306 }
307
308 pub fn get(self) -> u32 {
310 let NonZeroOrMaxU32(t) = self;
311 t
312 }
313 }
314}
315
316pub use private::*;
317
318#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
326pub enum NonZeroTimeValue {
327 Finite(NonZeroOrMaxU32),
329 Infinity,
334}
335
336impl From<NonZeroTimeValue> for TimeValue {
337 fn from(v: NonZeroTimeValue) -> TimeValue {
338 TimeValue::NonZero(v)
339 }
340}
341
342#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
349pub enum TimeValue {
350 Zero,
352 NonZero(NonZeroTimeValue),
354}
355
356impl TimeValue {
357 pub const fn new(t: u32) -> TimeValue {
359 match t {
360 0 => TimeValue::Zero,
361 u32::MAX => TimeValue::NonZero(NonZeroTimeValue::Infinity),
362 t => TimeValue::NonZero(NonZeroTimeValue::Finite(
363 NonZeroOrMaxU32::new(t).unwrap(),
365 )),
366 }
367 }
368}
369
370impl<'a, B: SplitByteSlice> IanaData<B> {
371 fn new(buf: B, context: OptionParseContext) -> Result<Self, ParseError> {
373 let buf_len = buf.len();
374 let (header, options) =
375 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
376 ParseError::InvalidOpLen(OptionCode::Iana, buf_len)
377 })?;
378 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, context)?;
379 Ok(IanaData { header, options })
380 }
381
382 pub fn iaid(&self) -> u32 {
384 self.header.iaid.get()
385 }
386
387 pub fn t1(&self) -> TimeValue {
394 TimeValue::new(self.header.t1.get())
395 }
396
397 pub fn t2(&self) -> TimeValue {
404 TimeValue::new(self.header.t2.get())
405 }
406
407 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
409 self.options.iter()
410 }
411}
412
413#[derive(
415 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
416)]
417#[repr(C)]
418struct IanaHeader {
419 iaid: U32,
420 t1: U32,
421 t2: U32,
422}
423
424#[derive(Debug, PartialEq)]
426pub struct IaAddrData<B: SplitByteSlice> {
427 header: Ref<B, IaAddrHeader>,
428 options: Records<B, ParsedDhcpOptionImpl>,
429}
430
431impl<'a, B: SplitByteSlice> IaAddrData<B> {
432 fn new(buf: B, context: OptionParseContext) -> Result<Self, ParseError> {
434 let buf_len = buf.len();
435 let (header, options) =
436 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
437 ParseError::InvalidOpLen(OptionCode::IaAddr, buf_len)
438 })?;
439 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, context)?;
440 Ok(IaAddrData { header, options })
441 }
442
443 pub fn addr(&self) -> Ipv6Addr {
445 self.header.addr
446 }
447
448 pub fn preferred_lifetime(&self) -> TimeValue {
455 TimeValue::new(self.header.preferred_lifetime.get())
456 }
457
458 pub fn valid_lifetime(&self) -> TimeValue {
465 TimeValue::new(self.header.valid_lifetime.get())
466 }
467
468 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
470 self.options.iter()
471 }
472}
473
474#[derive(
476 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
477)]
478#[repr(C)]
479struct IaAddrHeader {
480 addr: Ipv6Addr,
481 preferred_lifetime: U32,
482 valid_lifetime: U32,
483}
484
485#[derive(
487 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
488)]
489#[repr(C)]
490struct IaPdHeader {
491 iaid: U32,
492 t1: U32,
493 t2: U32,
494}
495
496#[derive(Debug, PartialEq)]
500pub struct IaPdData<B: SplitByteSlice> {
501 header: Ref<B, IaPdHeader>,
502 options: Records<B, ParsedDhcpOptionImpl>,
503}
504
505impl<'a, B: SplitByteSlice> IaPdData<B> {
506 fn new(buf: B, context: OptionParseContext) -> Result<Self, ParseError> {
508 let buf_len = buf.len();
509 let (header, options) =
510 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
511 ParseError::InvalidOpLen(OptionCode::IaPd, buf_len)
512 })?;
513 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, context)?;
514 Ok(IaPdData { header, options })
515 }
516
517 pub fn iaid(&self) -> u32 {
519 self.header.iaid.get()
520 }
521
522 pub fn t1(&self) -> TimeValue {
529 TimeValue::new(self.header.t1.get())
530 }
531
532 pub fn t2(&self) -> TimeValue {
539 TimeValue::new(self.header.t2.get())
540 }
541
542 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
544 self.options.iter()
545 }
546}
547
548#[derive(
550 KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug, PartialEq, Copy, Clone,
551)]
552#[repr(C)]
553struct IaPrefixHeader {
554 preferred_lifetime_secs: U32,
555 valid_lifetime_secs: U32,
556 prefix_length: u8,
557 prefix: Ipv6Addr,
558}
559
560#[derive(Debug, PartialEq)]
564pub struct IaPrefixData<B: SplitByteSlice> {
565 header: Ref<B, IaPrefixHeader>,
566 options: Records<B, ParsedDhcpOptionImpl>,
567}
568
569impl<'a, B: SplitByteSlice> IaPrefixData<B> {
570 fn new(buf: B, context: OptionParseContext) -> Result<Self, ParseError> {
572 let buf_len = buf.len();
573 let (header, options) =
574 Ref::from_prefix(buf).map_err(Into::into).map_err(|_: zerocopy::SizeError<_, _>| {
575 ParseError::InvalidOpLen(OptionCode::IaPrefix, buf_len)
576 })?;
577 let options = Records::<B, ParsedDhcpOptionImpl>::parse_with_context(options, context)?;
578 Ok(IaPrefixData { header, options })
579 }
580
581 pub fn prefix(&self) -> Result<Subnet<Ipv6Addr>, PrefixTooLongError> {
583 Subnet::from_host(self.header.prefix, self.header.prefix_length)
584 }
585
586 pub fn preferred_lifetime(&self) -> TimeValue {
593 TimeValue::new(self.header.preferred_lifetime_secs.get())
594 }
595
596 pub fn valid_lifetime(&self) -> TimeValue {
603 TimeValue::new(self.header.valid_lifetime_secs.get())
604 }
605
606 pub fn iter_options(&'a self) -> impl 'a + Iterator<Item = ParsedDhcpOption<'a>> {
608 self.options.iter()
609 }
610}
611
612mod checked {
613 use std::str::FromStr;
614
615 use mdns::protocol::{DomainBuilder, EmbeddedPacketBuilder};
616 use packet::BufferViewMut;
617 use zerocopy::SplitByteSliceMut;
618
619 use super::ParseError;
620
621 #[derive(Debug, PartialEq)]
623 pub struct Domain {
624 domain: String,
625 builder: DomainBuilder,
626 }
627
628 impl FromStr for Domain {
629 type Err = ParseError;
630
631 fn from_str(s: &str) -> Result<Self, ParseError> {
635 Self::try_from(s.to_string())
636 }
637 }
638
639 impl TryFrom<String> for Domain {
640 type Error = ParseError;
641
642 fn try_from(domain: String) -> Result<Self, ParseError> {
650 let builder = DomainBuilder::from_str(&domain).map_err(ParseError::DomainParseError)?;
651 Ok(Domain { domain, builder })
652 }
653 }
654
655 impl Domain {
656 pub(crate) fn bytes_len(&self) -> usize {
657 self.builder.bytes_len()
658 }
659
660 pub(crate) fn serialize<B: SplitByteSliceMut, BV: BufferViewMut<B>>(&self, bv: &mut BV) {
661 let () = self.builder.serialize(bv);
662 }
663 }
664}
665
666macro_rules! option_to_code {
667 ($option:ident, $($option_name:ident::$variant:tt($($v:tt)*)),*) => {
668 match $option {
669 $($option_name::$variant($($v)*)=>OptionCode::$variant,)*
670 }
671 }
672}
673
674impl ParsedDhcpOption<'_> {
675 pub fn code(&self) -> OptionCode {
677 option_to_code!(
678 self,
679 ParsedDhcpOption::ClientId(_),
680 ParsedDhcpOption::ServerId(_),
681 ParsedDhcpOption::Iana(_),
682 ParsedDhcpOption::IaAddr(_),
683 ParsedDhcpOption::Oro(_),
684 ParsedDhcpOption::Preference(_),
685 ParsedDhcpOption::ElapsedTime(_),
686 ParsedDhcpOption::StatusCode(_, _),
687 ParsedDhcpOption::IaPd(_),
688 ParsedDhcpOption::IaPrefix(_),
689 ParsedDhcpOption::InformationRefreshTime(_),
690 ParsedDhcpOption::SolMaxRt(_),
691 ParsedDhcpOption::DnsServers(_),
692 ParsedDhcpOption::DomainList(_)
693 )
694 }
695}
696
697type Duid = [u8];
701
702#[derive(Debug, PartialEq)]
709enum ParsedDhcpOptionImpl {}
710
711impl RecordsImplLayout for ParsedDhcpOptionImpl {
712 type Context = OptionParseContext;
713
714 type Error = ParseError;
715}
716
717impl RecordsImpl for ParsedDhcpOptionImpl {
718 type Record<'a> = ParsedDhcpOption<'a>;
719
720 fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
726 data: &mut BV,
727 context: &mut Self::Context,
728 ) -> RecordParseResult<Self::Record<'a>, Self::Error> {
729 let context = OptionParseContext { depth: context.depth + 1 };
730 if context.depth > MAX_RECURSION_DEPTH {
731 return Err(ParseError::OptionRecursionLimitExceeded);
732 }
733
734 if data.len() == 0 {
735 return Ok(ParsedRecord::Done);
736 }
737
738 let opt_code = data.take_obj_front::<U16>().ok_or(ParseError::BufferExhausted)?;
739 let opt_len = data.take_obj_front::<U16>().ok_or(ParseError::BufferExhausted)?;
740 let opt_len = usize::from(opt_len.get());
741 let mut opt_val = data.take_front(opt_len).ok_or(ParseError::BufferExhausted)?;
742
743 let opt_code = match OptionCode::try_from(opt_code.get()) {
744 Ok(opt_code) => opt_code,
745 Err(ParseError::InvalidOpCode(_)) => {
748 return Ok(ParsedRecord::Skipped);
752 }
753 Err(e) => unreachable!("unexpected error from op code conversion: {}", e),
754 };
755
756 let opt = match opt_code {
757 OptionCode::ClientId => Ok(ParsedDhcpOption::ClientId(opt_val)),
758 OptionCode::ServerId => Ok(ParsedDhcpOption::ServerId(opt_val)),
759 OptionCode::Iana => IanaData::new(opt_val, context).map(ParsedDhcpOption::Iana),
760 OptionCode::IaAddr => IaAddrData::new(opt_val, context).map(ParsedDhcpOption::IaAddr),
761 OptionCode::Oro => {
762 let options = opt_val
763 .chunks(2)
765 .map(|opt| {
766 let opt: [u8; 2] = opt.try_into().map_err(
767 |std::array::TryFromSliceError { .. }| {
768 ParseError::InvalidOpLen(OptionCode::Oro, opt_val.len())
769 },
770 )?;
771 OptionCode::try_from(u16::from_be_bytes(opt))
772 })
773 .collect::<Result<_, ParseError>>()?;
774 Ok(ParsedDhcpOption::Oro(options))
775 }
776 OptionCode::Preference => match opt_val {
777 &[b] => Ok(ParsedDhcpOption::Preference(b)),
778 opt_val => Err(ParseError::InvalidOpLen(OptionCode::Preference, opt_val.len())),
779 },
780 OptionCode::ElapsedTime => match opt_val {
781 &[b0, b1] => Ok(ParsedDhcpOption::ElapsedTime(u16::from_be_bytes([b0, b1]))),
782 opt_val => Err(ParseError::InvalidOpLen(OptionCode::ElapsedTime, opt_val.len())),
783 },
784 OptionCode::StatusCode => {
785 let mut opt_val = &mut opt_val;
786 let code = (&mut opt_val).take_obj_front::<U16>().ok_or_else(|| {
787 ParseError::InvalidOpLen(OptionCode::StatusCode, opt_val.len())
788 })?;
789 let message = str::from_utf8(opt_val)?;
790 Ok(ParsedDhcpOption::StatusCode(*code, message))
791 }
792 OptionCode::IaPd => IaPdData::new(opt_val, context).map(ParsedDhcpOption::IaPd),
793 OptionCode::IaPrefix => {
794 IaPrefixData::new(opt_val, context).map(ParsedDhcpOption::IaPrefix)
795 }
796 OptionCode::InformationRefreshTime => match opt_val {
797 &[b0, b1, b2, b3] => {
798 Ok(ParsedDhcpOption::InformationRefreshTime(u32::from_be_bytes([
799 b0, b1, b2, b3,
800 ])))
801 }
802 opt_val => {
803 Err(ParseError::InvalidOpLen(OptionCode::InformationRefreshTime, opt_val.len()))
804 }
805 },
806 OptionCode::SolMaxRt => {
807 let mut opt_val = &mut opt_val;
808 let sol_max_rt = (&mut opt_val)
809 .take_obj_front::<U32>()
810 .ok_or_else(|| ParseError::InvalidOpLen(OptionCode::SolMaxRt, opt_val.len()))?;
811 Ok(ParsedDhcpOption::SolMaxRt(*sol_max_rt))
812 }
813 OptionCode::DnsServers => {
814 let addresses = opt_val
815 .chunks(16)
817 .map(|opt| {
818 let opt: [u8; 16] = opt.try_into().map_err(
819 |std::array::TryFromSliceError { .. }| {
820 ParseError::InvalidOpLen(OptionCode::DnsServers, opt_val.len())
821 },
822 )?;
823 Ok(Ipv6Addr::from(opt))
824 })
825 .collect::<Result<_, ParseError>>()?;
826 Ok(ParsedDhcpOption::DnsServers(addresses))
827 }
828 OptionCode::DomainList => {
829 let mut opt_val = &mut opt_val;
830 let mut domains = Vec::new();
831 while opt_val.len() > 0 {
832 domains.push(checked::Domain::try_from(
833 Domain::parse(
834 &mut opt_val,
835 None,
841 )
842 .map_err(ParseError::DomainParseError)?
843 .to_string(),
844 )?);
845 }
846 Ok(ParsedDhcpOption::DomainList(domains))
847 }
848 }?;
849
850 Ok(ParsedRecord::Parsed(opt))
851 }
852}
853
854pub fn duid_uuid() -> [u8; 18] {
858 let mut duid = [0u8; 18];
859 duid[1] = 4;
860 let uuid = Uuid::new_v4();
861 let uuid = uuid.as_bytes();
862 duid[2..].copy_from_slice(&uuid[..]);
863 duid
864}
865
866#[allow(missing_docs)]
874#[derive(Debug)]
875pub enum DhcpOption<'a> {
876 ClientId(&'a Duid),
878 ServerId(&'a Duid),
880 Iana(IanaSerializer<'a>),
884 IaAddr(IaAddrSerializer<'a>),
888 Oro(&'a [OptionCode]),
893 Preference(u8),
895 ElapsedTime(u16),
897 StatusCode(u16, &'a str),
899 IaPd(IaPdSerializer<'a>),
901 IaPrefix(IaPrefixSerializer<'a>),
903 InformationRefreshTime(u32),
905 SolMaxRt(u32),
907 DnsServers(&'a [Ipv6Addr]),
909 DomainList(&'a [checked::Domain]),
911}
912
913#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
917pub struct IAID(u32);
918
919impl IAID {
920 pub const fn new(iaid: u32) -> Self {
922 Self(iaid)
923 }
924
925 pub fn get(&self) -> u32 {
927 let IAID(iaid) = self;
928 *iaid
929 }
930}
931
932#[derive(Debug)]
934pub struct IanaSerializer<'a> {
935 header: IanaHeader,
936 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
937}
938
939impl<'a> IanaSerializer<'a> {
940 pub fn new(iaid: IAID, t1: u32, t2: u32, options: &'a [DhcpOption<'a>]) -> IanaSerializer<'a> {
942 IanaSerializer {
943 header: IanaHeader { iaid: U32::new(iaid.get()), t1: U32::new(t1), t2: U32::new(t2) },
944 options: RecordSequenceBuilder::new(options.iter()),
945 }
946 }
947}
948
949#[derive(Debug)]
951pub struct IaAddrSerializer<'a> {
952 header: IaAddrHeader,
953 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
954}
955
956impl<'a> IaAddrSerializer<'a> {
957 pub fn new(
959 addr: Ipv6Addr,
960 preferred_lifetime: u32,
961 valid_lifetime: u32,
962 options: &'a [DhcpOption<'a>],
963 ) -> IaAddrSerializer<'a> {
964 IaAddrSerializer {
965 header: IaAddrHeader {
966 addr,
967 preferred_lifetime: U32::new(preferred_lifetime),
968 valid_lifetime: U32::new(valid_lifetime),
969 },
970 options: RecordSequenceBuilder::new(options.iter()),
971 }
972 }
973}
974
975#[derive(Debug)]
977pub struct IaPdSerializer<'a> {
978 header: IaPdHeader,
979 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
980}
981
982impl<'a> IaPdSerializer<'a> {
983 pub fn new(iaid: IAID, t1: u32, t2: u32, options: &'a [DhcpOption<'a>]) -> IaPdSerializer<'a> {
985 IaPdSerializer {
986 header: IaPdHeader { iaid: U32::new(iaid.get()), t1: U32::new(t1), t2: U32::new(t2) },
987 options: RecordSequenceBuilder::new(options.iter()),
988 }
989 }
990}
991
992#[derive(Debug)]
994pub struct IaPrefixSerializer<'a> {
995 header: IaPrefixHeader,
996 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
997}
998
999impl<'a> IaPrefixSerializer<'a> {
1000 pub fn new(
1002 preferred_lifetime_secs: u32,
1003 valid_lifetime_secs: u32,
1004 prefix: Subnet<Ipv6Addr>,
1005 options: &'a [DhcpOption<'a>],
1006 ) -> IaPrefixSerializer<'a> {
1007 IaPrefixSerializer {
1008 header: IaPrefixHeader {
1009 preferred_lifetime_secs: U32::new(preferred_lifetime_secs),
1010 valid_lifetime_secs: U32::new(valid_lifetime_secs),
1011 prefix_length: prefix.prefix(),
1012 prefix: prefix.network(),
1013 },
1014 options: RecordSequenceBuilder::new(options.iter()),
1015 }
1016 }
1017}
1018
1019impl DhcpOption<'_> {
1020 pub fn code(&self) -> OptionCode {
1022 option_to_code!(
1023 self,
1024 DhcpOption::ClientId(_),
1025 DhcpOption::ServerId(_),
1026 DhcpOption::Iana(_),
1027 DhcpOption::IaAddr(_),
1028 DhcpOption::Oro(_),
1029 DhcpOption::Preference(_),
1030 DhcpOption::ElapsedTime(_),
1031 DhcpOption::StatusCode(_, _),
1032 DhcpOption::IaPd(_),
1033 DhcpOption::IaPrefix(_),
1034 DhcpOption::InformationRefreshTime(_),
1035 DhcpOption::SolMaxRt(_),
1036 DhcpOption::DnsServers(_),
1037 DhcpOption::DomainList(_)
1038 )
1039 }
1040}
1041
1042impl<'a> RecordBuilder for DhcpOption<'a> {
1043 fn serialized_len(&self) -> usize {
1051 4 + match self {
1052 DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => {
1053 u16::try_from(duid.len()).unwrap_or(18).into()
1054 }
1055 DhcpOption::Iana(IanaSerializer { header, options }) => {
1056 u16::try_from(header.as_bytes().len() + options.serialized_len())
1057 .expect("overflows")
1058 .into()
1059 }
1060 DhcpOption::IaAddr(IaAddrSerializer { header, options }) => {
1061 u16::try_from(header.as_bytes().len() + options.serialized_len())
1062 .expect("overflows")
1063 .into()
1064 }
1065 DhcpOption::Oro(opts) => u16::try_from(2 * opts.len()).unwrap_or(0).into(),
1066 DhcpOption::Preference(v) => std::mem::size_of_val(v),
1067 DhcpOption::ElapsedTime(v) => std::mem::size_of_val(v),
1068 DhcpOption::StatusCode(v, message) => std::mem::size_of_val(v) + message.len(),
1069 DhcpOption::IaPd(IaPdSerializer { header, options }) => {
1070 u16::try_from(header.as_bytes().len() + options.serialized_len())
1071 .expect("overflows")
1072 .into()
1073 }
1074 DhcpOption::IaPrefix(IaPrefixSerializer { header, options }) => {
1075 u16::try_from(header.as_bytes().len() + options.serialized_len())
1076 .expect("overflows")
1077 .into()
1078 }
1079 DhcpOption::InformationRefreshTime(v) => std::mem::size_of_val(v),
1080 DhcpOption::SolMaxRt(v) => std::mem::size_of_val(v),
1081 DhcpOption::DnsServers(recursive_name_servers) => {
1082 u16::try_from(16 * recursive_name_servers.len()).unwrap_or(0).into()
1083 }
1084 DhcpOption::DomainList(domains) => {
1085 u16::try_from(domains.iter().fold(0, |tot, domain| tot + domain.bytes_len()))
1086 .unwrap_or(0)
1087 .into()
1088 }
1089 }
1090 }
1091
1092 fn serialize_into(&self, mut buf: &mut [u8]) {
1107 let mut buf = &mut buf;
1109 let () = buf.write_obj_front(&U16::new(self.code().into())).expect("buffer is too small");
1110
1111 match self {
1112 DhcpOption::ClientId(duid) | DhcpOption::ServerId(duid) => {
1113 match u16::try_from(duid.len()) {
1114 Ok(len) => {
1115 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1116 let () = buf.write_obj_front(*duid).expect("buffer is too small");
1117 }
1118 Err(std::num::TryFromIntError { .. }) => {
1119 let duid = duid_uuid();
1122 let len = u16::try_from(duid.len()).expect("uuid length is too long");
1123 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1124 let () = buf.write_obj_front(&duid).expect("buffer is too small");
1125 }
1126 }
1127 }
1128 DhcpOption::Iana(IanaSerializer { header, options }) => {
1129 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1130 .expect("overflows");
1131 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1132 let () = buf.write_obj_front(header).expect("buffer is too small");
1133 let () = options.serialize_into(buf);
1134 }
1135 DhcpOption::IaAddr(IaAddrSerializer { header, options }) => {
1136 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1137 .expect("overflows");
1138 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1139 let () = buf.write_obj_front(header).expect("buffer is too small");
1140 let () = options.serialize_into(buf);
1141 }
1142 DhcpOption::Oro(requested_opts) => {
1143 let (requested_opts, len) = u16::try_from(2 * requested_opts.len()).map_or_else(
1144 |std::num::TryFromIntError { .. }| {
1145 (&[][..], 0)
1148 },
1149 |len| (*requested_opts, len),
1150 );
1151 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1152 for opt_code in requested_opts.iter() {
1153 let () = buf
1154 .write_obj_front(&u16::from(*opt_code).to_be_bytes())
1155 .expect("buffer is too small");
1156 }
1157 }
1158 DhcpOption::Preference(pref_val) => {
1159 let () = buf.write_obj_front(&U16::new(1)).expect("buffer is too small");
1160 let () = buf.write_obj_front(pref_val).expect("buffer is too small");
1161 }
1162 DhcpOption::ElapsedTime(elapsed_time) => {
1163 let () = buf
1164 .write_obj_front(&U16::new(
1165 mem::size_of_val(elapsed_time).try_into().expect("overflows"),
1166 ))
1167 .expect("buffer is too small");
1168 let () =
1169 buf.write_obj_front(&U16::new(*elapsed_time)).expect("buffer is too small");
1170 }
1171 DhcpOption::StatusCode(code, message) => {
1172 let opt_len = u16::try_from(2 + message.len()).expect("overflows");
1173 let () = buf.write_obj_front(&U16::new(opt_len)).expect("buffer is too small");
1174 let () = buf.write_obj_front(&U16::new(*code)).expect("buffer is too small");
1175 let () = buf.write_obj_front(message.as_bytes()).expect("buffer is too small");
1176 }
1177 DhcpOption::IaPd(IaPdSerializer { header, options }) => {
1178 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1179 .expect("overflows");
1180 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1181 buf.write_obj_front(header).expect("buffer is too small");
1182 let () = options.serialize_into(buf);
1183 }
1184 DhcpOption::IaPrefix(IaPrefixSerializer { header, options }) => {
1185 let len = u16::try_from(header.as_bytes().len() + options.serialized_len())
1186 .expect("overflows");
1187 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1188 buf.write_obj_front(header).expect("buffer is too small");
1189 let () = options.serialize_into(buf);
1190 }
1191 DhcpOption::InformationRefreshTime(information_refresh_time) => {
1192 let () = buf
1193 .write_obj_front(&U16::new(
1194 mem::size_of_val(information_refresh_time).try_into().expect("overflows"),
1195 ))
1196 .expect("buffer is too small");
1197 let () = buf
1198 .write_obj_front(&U32::new(*information_refresh_time))
1199 .expect("buffer is too small");
1200 }
1201 DhcpOption::SolMaxRt(sol_max_rt) => {
1202 let () = buf
1203 .write_obj_front(&U16::new(
1204 mem::size_of_val(sol_max_rt).try_into().expect("overflows"),
1205 ))
1206 .expect("buffer is too small");
1207 let () = buf.write_obj_front(&U32::new(*sol_max_rt)).expect("buffer is too small");
1208 }
1209 DhcpOption::DnsServers(recursive_name_servers) => {
1210 let (recursive_name_servers, len) =
1211 u16::try_from(16 * recursive_name_servers.len()).map_or_else(
1212 |std::num::TryFromIntError { .. }| {
1213 (&[][..], 0)
1216 },
1217 |len| (*recursive_name_servers, len),
1218 );
1219 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1220 recursive_name_servers.iter().for_each(|server_addr| {
1221 let () = buf.write_obj_front(server_addr.bytes()).expect("buffer is too small");
1222 })
1223 }
1224 DhcpOption::DomainList(domains) => {
1225 let (domains, len) =
1226 u16::try_from(domains.iter().map(|domain| domain.bytes_len()).sum::<usize>())
1227 .map_or_else(
1228 |std::num::TryFromIntError { .. }| {
1229 (&[][..], 0)
1232 },
1233 |len| (*domains, len),
1234 );
1235 let () = buf.write_obj_front(&U16::new(len)).expect("buffer is too small");
1236 domains.iter().for_each(|domain| {
1237 domain.serialize(&mut buf);
1238 })
1239 }
1240 }
1241 }
1242}
1243
1244type TransactionId = [u8; 3];
1248
1249#[derive(Debug)]
1253pub struct Message<'a, B> {
1254 msg_type: MessageType,
1255 transaction_id: &'a TransactionId,
1256 options: Records<B, ParsedDhcpOptionImpl>,
1257}
1258
1259impl<'a, B: SplitByteSlice> Message<'a, B> {
1260 pub fn msg_type(&self) -> MessageType {
1262 self.msg_type
1263 }
1264
1265 pub fn transaction_id(&self) -> &TransactionId {
1267 &self.transaction_id
1268 }
1269
1270 pub fn options<'b: 'a>(&'b self) -> impl 'b + Iterator<Item = ParsedDhcpOption<'a>> {
1272 self.options.iter()
1273 }
1274}
1275
1276impl<'a, B: 'a + SplitByteSlice + IntoByteSlice<'a>> ParsablePacket<B, ()> for Message<'a, B> {
1277 type Error = ParseError;
1278
1279 fn parse_metadata(&self) -> ParseMetadata {
1280 let Self { msg_type, transaction_id, options } = self;
1281 ParseMetadata::from_packet(
1282 0,
1283 mem::size_of_val(msg_type) + mem::size_of_val(transaction_id) + options.bytes().len(),
1284 0,
1285 )
1286 }
1287
1288 fn parse<BV: BufferView<B>>(mut buf: BV, _args: ()) -> Result<Self, ParseError> {
1289 let msg_type =
1290 MessageType::try_from(buf.take_byte_front().ok_or(ParseError::BufferExhausted)?)?;
1291 let transaction_id = Ref::into_ref(
1292 buf.take_obj_front::<TransactionId>().ok_or(ParseError::BufferExhausted)?,
1293 );
1294 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(
1295 buf.take_rest_front(),
1296 OptionParseContext { depth: 0 },
1297 )?;
1298 Ok(Message { msg_type, transaction_id, options })
1299 }
1300}
1301
1302#[derive(Debug)]
1309pub struct MessageBuilder<'a> {
1310 msg_type: MessageType,
1311 transaction_id: TransactionId,
1312 options: RecordSequenceBuilder<DhcpOption<'a>, Iter<'a, DhcpOption<'a>>>,
1313}
1314
1315impl<'a> MessageBuilder<'a> {
1316 pub fn new(
1318 msg_type: MessageType,
1319 transaction_id: TransactionId,
1320 options: &'a [DhcpOption<'a>],
1321 ) -> MessageBuilder<'a> {
1322 MessageBuilder {
1323 msg_type,
1324 transaction_id,
1325 options: RecordSequenceBuilder::new(options.iter()),
1326 }
1327 }
1328}
1329
1330impl InnerPacketBuilder for MessageBuilder<'_> {
1331 fn bytes_len(&self) -> usize {
1336 let Self { msg_type, transaction_id, options } = self;
1337 mem::size_of_val(msg_type) + mem::size_of_val(transaction_id) + options.serialized_len()
1338 }
1339
1340 fn serialize(&self, mut buffer: &mut [u8]) {
1348 let Self { msg_type, transaction_id, options } = self;
1349 let mut buffer = &mut buffer;
1351 let () = buffer.write_obj_front(msg_type).expect("buffer is too small");
1352 let () = buffer.write_obj_front(transaction_id).expect("buffer is too small");
1353 let () = options.serialize_into(buffer);
1354 }
1355}
1356
1357#[cfg(test)]
1358mod tests {
1359 use super::*;
1360 use assert_matches::assert_matches;
1361 use net_declare::{net_ip_v6, net_subnet_v6};
1362 use std::str::FromStr;
1363 use test_case::test_case;
1364
1365 fn test_buf_with_no_options() -> Vec<u8> {
1366 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &[]);
1367 let mut buf = vec![0; builder.bytes_len()];
1368 let () = builder.serialize(&mut buf);
1369 buf
1370 }
1371
1372 #[test]
1373 fn test_message_serialization() {
1374 let iaaddr_options = [DhcpOption::StatusCode(0, "Success.")];
1375 let iana_options = [
1376 DhcpOption::Preference(42),
1377 DhcpOption::IaAddr(IaAddrSerializer::new(
1378 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1379 3600,
1380 7200,
1381 &iaaddr_options,
1382 )),
1383 ];
1384 let iaprefix_options = [DhcpOption::StatusCode(0, "Success.")];
1385 let iapd_options = [DhcpOption::IaPrefix(IaPrefixSerializer::new(
1386 9999,
1387 6666,
1388 net_subnet_v6!("abcd:1234::/56"),
1389 &iaprefix_options,
1390 ))];
1391 let dns_servers = [
1392 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1393 Ipv6Addr::from([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]),
1394 ];
1395 let domains = [
1396 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1397 checked::Domain::from_str("www.google.com").expect("failed to construct test domain"),
1398 ];
1399 let options = [
1400 DhcpOption::ClientId(&[4, 5, 6]),
1401 DhcpOption::ServerId(&[8]),
1402 DhcpOption::Iana(IanaSerializer::new(IAID::new(42), 3000, 6500, &iana_options)),
1403 DhcpOption::Oro(&[OptionCode::ClientId, OptionCode::ServerId]),
1404 DhcpOption::Preference(42),
1405 DhcpOption::ElapsedTime(3600),
1406 DhcpOption::StatusCode(0, "Success."),
1407 DhcpOption::IaPd(IaPdSerializer::new(IAID::new(44), 5000, 7000, &iapd_options)),
1408 DhcpOption::InformationRefreshTime(86400),
1409 DhcpOption::SolMaxRt(86400),
1410 DhcpOption::DnsServers(&dns_servers),
1411 DhcpOption::DomainList(&domains),
1412 ];
1413 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1414 assert_eq!(builder.bytes_len(), 256);
1415 let mut buf = vec![0; builder.bytes_len()];
1416 let () = builder.serialize(&mut buf);
1417
1418 #[rustfmt::skip]
1419 assert_eq!(
1420 buf[..],
1421 [
1422 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,
1428 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,
1433
1434 0, 25, 0, 55,
1436 0, 0, 0, 44,
1438 0, 0, 19, 136,
1440 0, 0, 27, 88,
1442 0, 26, 0, 39,
1445 0, 0, 39, 15,
1447 0, 0, 26, 10,
1449 56,
1451 0xab, 0xcd, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00,
1453 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1454 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1457
1458 0, 32, 0, 4, 0, 1, 81, 128, 0, 82, 0, 4, 0, 1, 81, 128, 0, 23, 0, 32,
1462 0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215,
1463 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
1464 0, 24, 0, 29,
1466 7, 102, 117, 99, 104, 115, 105, 97, 3, 100, 101, 118, 0,
1467 3, 119, 119, 119, 6, 103, 111, 111, 103, 108, 101, 3, 99, 111, 109, 0
1468 ],
1469 );
1470 }
1471
1472 #[test]
1473 fn test_message_serialization_parsing_roundtrip() {
1474 let iaaddr_suboptions = [DhcpOption::StatusCode(0, "Success.")];
1475 let iana_suboptions = [
1476 DhcpOption::Preference(42),
1477 DhcpOption::IaAddr(IaAddrSerializer::new(
1478 Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215]),
1479 7200,
1480 9000,
1481 &iaaddr_suboptions,
1482 )),
1483 ];
1484 let iaprefix_options = [DhcpOption::StatusCode(0, "Success.")];
1485 let iapd_options = [DhcpOption::IaPrefix(IaPrefixSerializer::new(
1486 89658902,
1487 82346231,
1488 net_subnet_v6!("1234:5678:1231::/48"),
1489 &iaprefix_options,
1490 ))];
1491 let dns_servers = [net_ip_v6!("::")];
1492 let domains = [
1493 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1494 checked::Domain::from_str("www.google.com").expect("failed to construct test domain"),
1495 ];
1496 let options = [
1497 DhcpOption::ClientId(&[4, 5, 6]),
1498 DhcpOption::ServerId(&[8]),
1499 DhcpOption::Iana(IanaSerializer::new(IAID::new(1234), 7000, 8800, &iana_suboptions)),
1500 DhcpOption::Oro(&[OptionCode::ClientId, OptionCode::ServerId]),
1501 DhcpOption::Preference(42),
1502 DhcpOption::ElapsedTime(3600),
1503 DhcpOption::StatusCode(0, "Success."),
1504 DhcpOption::IaPd(IaPdSerializer::new(IAID::new(1412), 6513, 9876, &iapd_options)),
1505 DhcpOption::InformationRefreshTime(86400),
1506 DhcpOption::SolMaxRt(86400),
1507 DhcpOption::DnsServers(&dns_servers),
1508 DhcpOption::DomainList(&domains),
1509 ];
1510 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1511 let mut buf = vec![0; builder.bytes_len()];
1512 let () = builder.serialize(&mut buf);
1513
1514 let mut buf = &buf[..];
1515 let msg = Message::parse(&mut buf, ()).expect("parse should succeed");
1516 assert_eq!(msg.msg_type, MessageType::Solicit);
1517 assert_eq!(msg.transaction_id, &[1, 2, 3]);
1518 let got_options: Vec<_> = msg.options.iter().collect();
1519
1520 let iana_buf = [
1521 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,
1522 5, 6, 107, 108, 109, 110, 111, 212, 213, 214, 215, 0, 0, 28, 32, 0, 0, 35, 40, 0, 13,
1523 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1524 ];
1525 let iapd_buf = [
1526 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,
1536 0x00, 0x00, 0, 13, 0, 10, 0, 0, 83, 117, 99, 99, 101, 115, 115, 46,
1538 ];
1539 let options = [
1540 ParsedDhcpOption::ClientId(&[4, 5, 6]),
1541 ParsedDhcpOption::ServerId(&[8]),
1542 ParsedDhcpOption::Iana(
1543 IanaData::new(&iana_buf[..], OptionParseContext { depth: 1 })
1544 .expect("construction failed"),
1545 ),
1546 ParsedDhcpOption::Oro(vec![OptionCode::ClientId, OptionCode::ServerId]),
1547 ParsedDhcpOption::Preference(42),
1548 ParsedDhcpOption::ElapsedTime(3600),
1549 ParsedDhcpOption::StatusCode(U16::new(0), "Success."),
1550 ParsedDhcpOption::IaPd(
1551 IaPdData::new(&iapd_buf[..], OptionParseContext { depth: 1 })
1552 .expect("IA_PD construction failed"),
1553 ),
1554 ParsedDhcpOption::InformationRefreshTime(86400),
1555 ParsedDhcpOption::SolMaxRt(U32::new(86400)),
1556 ParsedDhcpOption::DnsServers(vec![Ipv6Addr::from([0; 16])]),
1557 ParsedDhcpOption::DomainList(vec![
1558 checked::Domain::from_str("fuchsia.dev").expect("failed to construct test domain"),
1559 checked::Domain::from_str("www.google.com")
1560 .expect("failed to construct test domain"),
1561 ]),
1562 ];
1563 assert_eq!(got_options, options);
1564 }
1565
1566 const OVERFLOW_LENGTH: usize = u16::MAX as usize + 1;
1568
1569 #[test]
1570 fn test_message_serialization_duid_too_long() {
1571 let options = [DhcpOption::ClientId(&[0u8; OVERFLOW_LENGTH])];
1572 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1573 let mut buf = vec![0; builder.bytes_len()];
1574 let () = builder.serialize(&mut buf);
1575
1576 assert_eq!(buf.len(), 26);
1577 assert_eq!(
1578 buf[..8],
1579 [
1580 1, 1, 2, 3, 0, 1, 0, 18, ],
1584 );
1585
1586 let mut buf = &buf[..];
1588 let _: Message<'_, _> = Message::parse(&mut buf, ()).expect("parse should succeed");
1589 }
1590
1591 #[test]
1592 fn test_message_serialization_oro_too_long() {
1593 let options = [DhcpOption::Oro(&[OptionCode::Preference; OVERFLOW_LENGTH][..])];
1594 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &options);
1595 let mut buf = vec![0; builder.bytes_len()];
1596 let () = builder.serialize(&mut buf);
1597
1598 assert_eq!(
1599 buf[..],
1600 [
1601 1, 1, 2, 3, 0, 6, 0, 0, ],
1605 );
1606
1607 let mut buf = &buf[..];
1609 let _: Message<'_, _> = Message::parse(&mut buf, ()).expect("parse should succeed");
1610 }
1611
1612 #[test]
1613 fn test_option_serialization_parsing_roundtrip() {
1614 let mut buf = [0u8; 6];
1615 let option = DhcpOption::ElapsedTime(42);
1616
1617 option.serialize_into(&mut buf);
1618 assert_eq!(buf, [0, 8, 0, 2, 0, 42]);
1619
1620 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(
1621 &buf[..],
1622 OptionParseContext { depth: 0 },
1623 )
1624 .expect("parse should succeed");
1625 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1626 assert_eq!(options[..], [ParsedDhcpOption::ElapsedTime(42)]);
1627 }
1628
1629 #[test]
1630 fn test_buffer_too_short() {
1631 let buf = [];
1632 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1633
1634 let buf = [
1635 1, 0, ];
1638 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1639
1640 let buf = [
1641 1, 1, 2, 3, 0, ];
1645 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1646
1647 let buf = [
1648 1, 1, 2, 3, 0, 1, 0, ];
1653 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1654
1655 let buf = [
1657 1, 1, 2, 3, 0, 1, 0, 100, 1, 2, ];
1663 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::BufferExhausted));
1664 }
1665
1666 #[test]
1667 fn test_invalid_message_type() {
1668 let mut buf = test_buf_with_no_options();
1669 buf[0] = 0;
1671 assert_matches!(Message::parse(&mut &buf[..], ()), Err(ParseError::InvalidMessageType(0)));
1672 }
1673
1674 #[test]
1675 fn test_skip_invalid_op_code() {
1676 let mut buf = test_buf_with_no_options();
1677 buf.extend_from_slice(&[
1678 0, 0, 0, 1, 0, 0, 1, 0, 3, 4, 5, 6, ]);
1683 let mut buf = &buf[..];
1684 let msg = Message::parse(&mut buf, ()).expect("parse should succeed");
1685 let got_options: Vec<_> = msg.options.iter().collect();
1686 assert_eq!(got_options, [ParsedDhcpOption::ClientId(&[4, 5, 6])]);
1687 }
1688
1689 #[test]
1690 fn test_iana_no_suboptions_serialization_parsing_roundtrip() {
1691 let mut buf = [0u8; 16];
1692 let option = DhcpOption::Iana(IanaSerializer::new(IAID::new(3456), 1024, 54321, &[]));
1693
1694 option.serialize_into(&mut buf);
1695 assert_eq!(buf, [0, 3, 0, 12, 0, 0, 13, 128, 0, 0, 4, 0, 0, 0, 212, 49]);
1696
1697 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(
1698 &buf[..],
1699 OptionParseContext { depth: 0 },
1700 )
1701 .expect("parse should succeed");
1702 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1703 let iana_buf = [0, 0, 13, 128, 0, 0, 4, 0, 0, 0, 212, 49];
1704 assert_eq!(
1705 options[..],
1706 [ParsedDhcpOption::Iana(
1707 IanaData::new(&iana_buf[..], OptionParseContext { depth: 1 })
1708 .expect("construction failed")
1709 )]
1710 );
1711 }
1712
1713 #[test]
1717 fn test_iana_invalid_opt_len() {
1718 let mut buf = test_buf_with_no_options();
1719 buf.extend_from_slice(&[
1720 0, 3, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1723 ]);
1724 assert_matches!(
1725 Message::parse(&mut &buf[..], ()),
1726 Err(ParseError::InvalidOpLen(OptionCode::Iana, 8))
1727 );
1728 }
1729
1730 #[test]
1731 fn test_iaaddr_no_suboptions_serialization_parsing_roundtrip() {
1732 let mut buf = [0u8; 28];
1733 let option = DhcpOption::IaAddr(IaAddrSerializer::new(
1734 Ipv6Addr::from([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]),
1735 0,
1736 0,
1737 &[],
1738 ));
1739
1740 option.serialize_into(&mut buf);
1741 assert_eq!(
1742 buf,
1743 [
1744 0, 5, 0, 24, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0,
1745 0, 0, 0, 0, 0, 0
1746 ]
1747 );
1748
1749 let options = Records::<_, ParsedDhcpOptionImpl>::parse_with_context(
1750 &buf[..],
1751 OptionParseContext { depth: 0 },
1752 )
1753 .expect("parse should succeed");
1754 let options: Vec<ParsedDhcpOption<'_>> = options.iter().collect();
1755 let iaaddr_buf = [
1756 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 0, 0,
1757 ];
1758 assert_eq!(
1759 options[..],
1760 [ParsedDhcpOption::IaAddr(
1761 IaAddrData::new(&iaaddr_buf[..], OptionParseContext { depth: 1 })
1762 .expect("construction failed")
1763 )]
1764 );
1765 }
1766
1767 #[test]
1771 fn test_iaaddr_invalid_opt_len() {
1772 let mut buf = test_buf_with_no_options();
1773 buf.extend_from_slice(&[
1774 0, 5, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1777 ]);
1778 assert_matches!(
1779 Message::parse(&mut &buf[..], ()),
1780 Err(ParseError::InvalidOpLen(OptionCode::IaAddr, 8))
1781 );
1782 }
1783
1784 #[test]
1788 fn test_invalid_oro_opt_len() {
1789 let mut buf = test_buf_with_no_options();
1790 buf.extend_from_slice(&[
1791 0, 6, 0, 1, 0,
1794 ]);
1795 assert_matches!(
1796 Message::parse(&mut &buf[..], ()),
1797 Err(ParseError::InvalidOpLen(OptionCode::Oro, 1))
1798 );
1799 }
1800
1801 #[test]
1805 fn test_invalid_preference_opt_len() {
1806 let mut buf = test_buf_with_no_options();
1807 buf.extend_from_slice(&[
1808 0, 7, 0, 2, 0, 0,
1811 ]);
1812 assert_matches!(
1813 Message::parse(&mut &buf[..], ()),
1814 Err(ParseError::InvalidOpLen(OptionCode::Preference, 2))
1815 );
1816 }
1817
1818 #[test]
1822 fn test_elapsed_time_invalid_opt_len() {
1823 let mut buf = test_buf_with_no_options();
1824 buf.extend_from_slice(&[
1825 0, 8, 0, 3, 0, 0, 0,
1828 ]);
1829 assert_matches!(
1830 Message::parse(&mut &buf[..], ()),
1831 Err(ParseError::InvalidOpLen(OptionCode::ElapsedTime, 3))
1832 );
1833 }
1834
1835 #[test]
1839 fn test_status_code_invalid_opt_len() {
1840 let mut buf = test_buf_with_no_options();
1841 buf.extend_from_slice(&[
1842 0, 13, 0, 1, 0, 0, 0,
1845 ]);
1846 assert_matches!(
1847 Message::parse(&mut &buf[..], ()),
1848 Err(ParseError::InvalidOpLen(OptionCode::StatusCode, 1))
1849 );
1850 }
1851 #[test]
1855 fn test_information_refresh_time_invalid_opt_len() {
1856 let mut buf = test_buf_with_no_options();
1857 buf.extend_from_slice(&[
1858 0, 32, 0, 3, 0, 0, 0,
1861 ]);
1862 assert_matches!(
1863 Message::parse(&mut &buf[..], ()),
1864 Err(ParseError::InvalidOpLen(OptionCode::InformationRefreshTime, 3))
1865 );
1866 }
1867
1868 #[test]
1872 fn test_sol_max_rt_invalid_opt_len() {
1873 let mut buf = test_buf_with_no_options();
1874 buf.extend_from_slice(&[
1875 0, 82, 0, 3, 0, 0, 0,
1878 ]);
1879 assert_matches!(
1880 Message::parse(&mut &buf[..], ()),
1881 Err(ParseError::InvalidOpLen(OptionCode::SolMaxRt, 3))
1882 );
1883 }
1884 #[test]
1888 fn test_dns_servers_invalid_opt_len() {
1889 let mut buf = test_buf_with_no_options();
1890 buf.extend_from_slice(&[
1891 0, 23, 0, 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
1894 ]);
1895 assert_matches!(
1896 Message::parse(&mut &buf[..], ()),
1897 Err(ParseError::InvalidOpLen(OptionCode::DnsServers, 17))
1898 );
1899 }
1900
1901 #[test_case(TimeValue::new(0), TimeValue::Zero)]
1902 #[test_case(TimeValue::new(5), TimeValue::NonZero(NonZeroTimeValue::Finite(NonZeroOrMaxU32::new(5).expect("should succeed for non zero or u32::MAX values"))))]
1903 #[test_case(TimeValue::new(u32::MAX), TimeValue::NonZero(NonZeroTimeValue::Infinity))]
1904 fn test_time_value_new(time_value: TimeValue, expected_variant: TimeValue) {
1905 assert_eq!(time_value, expected_variant);
1906 }
1907
1908 #[test_case(
1909 NonZeroTimeValue::Finite(
1910 NonZeroOrMaxU32::new(1)
1911 .expect("should succeed for non zero or u32::MAX values")
1912 ))]
1913 #[test_case(NonZeroTimeValue::Infinity)]
1914 fn test_time_value_ord(non_zero_tv: NonZeroTimeValue) {
1915 assert!(TimeValue::Zero < TimeValue::NonZero(non_zero_tv));
1916 }
1917
1918 #[test]
1919 fn test_non_zero_time_value_ord() {
1920 assert!(
1921 NonZeroTimeValue::Finite(
1922 NonZeroOrMaxU32::new(u32::MAX - 1)
1923 .expect("should succeed for non zero or u32::MAX values")
1924 ) < NonZeroTimeValue::Infinity
1925 );
1926 }
1927
1928 #[test_case(0, None)]
1929 #[test_case(60, Some(NonZeroOrMaxU32::new(60).unwrap()))]
1930 #[test_case(u32::MAX, None)]
1931 fn test_non_zero_or_max_u32_new(t: u32, expected: Option<NonZeroOrMaxU32>) {
1932 assert_eq!(NonZeroOrMaxU32::new(t), expected);
1933 }
1934
1935 #[test_case(1)]
1936 #[test_case(4321)]
1937 #[test_case(u32::MAX - 1)]
1938 fn test_non_zero_or_max_u32_get(t: u32) {
1939 assert_eq!(
1940 NonZeroOrMaxU32::new(t).expect("should succeed for non-zero or u32::MAX values").get(),
1941 t
1942 );
1943 }
1944
1945 #[test]
1946 fn test_option_recursion_limit() {
1947 let lvl5 = [DhcpOption::StatusCode(0, "Success.")];
1948 let lvl4 = [DhcpOption::Iana(IanaSerializer::new(IAID::new(5), 0, 0, &lvl5))];
1949 let lvl3 = [DhcpOption::Iana(IanaSerializer::new(IAID::new(4), 0, 0, &lvl4))];
1950 let lvl2 = [DhcpOption::Iana(IanaSerializer::new(IAID::new(3), 0, 0, &lvl3))];
1951 let lvl1 = [DhcpOption::Iana(IanaSerializer::new(IAID::new(2), 0, 0, &lvl2))];
1952
1953 let builder = MessageBuilder::new(MessageType::Solicit, [1, 2, 3], &lvl1);
1954 let mut buf = vec![0; builder.bytes_len()];
1955 let () = builder.serialize(&mut buf);
1956
1957 let mut buf = &buf[..];
1958 assert_matches!(
1959 Message::parse(&mut buf, ()),
1960 Err(ParseError::OptionRecursionLimitExceeded)
1961 );
1962 }
1963}