1use bitflags::bitflags;
71use byteorder::{ByteOrder, NetworkEndian};
72use core::convert::{TryFrom, TryInto};
73use core::iter::FusedIterator;
74use core::{fmt, mem, slice};
75use memchr::memchr;
76use ref_cast::RefCast;
77
78use crate::{Cursor, Error};
79
80pub mod relay;
81
82pub const SERVER_PORT: u16 = 67;
84pub const CLIENT_PORT: u16 = 68;
86pub const MAX_MESSAGE_SIZE: usize = 576;
88
89#[derive(Clone, Copy, PartialEq, Eq, RefCast, Debug)]
94#[repr(transparent)]
95pub struct Addr(pub [u8; 4]);
96
97impl<'a> TryFrom<&'a [u8]> for &'a Addr {
98 type Error = Error;
99
100 #[inline]
101 fn try_from(b: &'a [u8]) -> Result<Self, Self::Error> {
102 b[..4]
103 .try_into()
104 .map(Addr::ref_cast)
105 .map_err(|_| Error::Malformed)
106 }
107}
108
109#[cfg(feature = "std")]
110impl From<Addr> for std::net::Ipv4Addr {
111 #[inline]
112 fn from(Addr(x): Addr) -> Self {
113 x.into()
114 }
115}
116
117#[cfg(feature = "std")]
118impl From<std::net::Ipv4Addr> for Addr {
119 #[inline]
120 fn from(x: std::net::Ipv4Addr) -> Self {
121 Addr(x.octets())
122 }
123}
124
125#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
127pub enum OpCode {
128 BootRequest = 1,
130 BootReply,
132}
133
134impl TryFrom<u8> for OpCode {
135 type Error = Error;
136
137 fn try_from(value: u8) -> Result<Self, Self::Error> {
138 Ok(match value {
139 1 => OpCode::BootRequest,
140 2 => OpCode::BootReply,
141 _ => return Err(Error::Malformed),
142 })
143 }
144}
145
146#[derive(Clone, Copy, PartialEq, Eq, Hash)]
148pub struct MessageType(u8);
149
150impl MessageType {
151 pub const DISCOVER: MessageType = MessageType(1);
152 pub const OFFER: MessageType = MessageType(2);
153 pub const REQUEST: MessageType = MessageType(3);
154 pub const DECLINE: MessageType = MessageType(4);
155 pub const ACK: MessageType = MessageType(5);
156 pub const NAK: MessageType = MessageType(6);
157 pub const RELEASE: MessageType = MessageType(7);
158 pub const INFORM: MessageType = MessageType(8);
159}
160
161impl From<u8> for MessageType {
162 fn from(x: u8) -> Self {
163 Self(x)
164 }
165}
166
167impl From<MessageType> for u8 {
168 fn from(MessageType(x): MessageType) -> Self {
169 x
170 }
171}
172
173impl fmt::Debug for MessageType {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 let name = match *self {
176 MessageType::DISCOVER => "DISCOVER",
177 MessageType::OFFER => "OFFER",
178 MessageType::REQUEST => "REQUEST",
179 MessageType::DECLINE => "DECLINE",
180 MessageType::ACK => "ACK",
181 MessageType::NAK => "NAK",
182 MessageType::RELEASE => "RELEASE",
183 MessageType::INFORM => "INFORM",
184 Self(x) => return f.debug_tuple("MessageType").field(&x).finish(),
185 };
186 f.write_str(name)
187 }
188}
189
190bitflags! {
191 #[repr(transparent)]
195 pub struct Flags: u16 {
196 const BROADCAST = 1 << 15;
197 }
198
199 #[repr(transparent)]
201 pub struct OptionOverload: u8 {
202 const FILE = 0b01;
204 const SNAME = 0b10;
206 }
207}
208
209#[derive(Clone, Copy, PartialEq, Eq, Debug)]
211#[non_exhaustive]
212pub enum DhcpOption<'a> {
213 Pad,
218 End,
222 SubnetMask(&'a Addr),
224 TimeOffset(i32),
226 Router(&'a [Addr]),
228 TimeServer(&'a [Addr]),
230 NameServer(&'a [Addr]),
232 DomainNameServer(&'a [Addr]),
234 LogServer(&'a [Addr]),
236 CookieServer(&'a [Addr]),
238 LprServer(&'a [Addr]),
240 ImpressServer(&'a [Addr]),
242 ResourceLocationServer(&'a [Addr]),
244 HostName(&'a [u8]),
246 BootFileSize(u16),
248 MeritDumpFile(&'a [u8]),
250 DomainName(&'a [u8]),
252 SwapServer(&'a Addr),
254 RootPath(&'a [u8]),
256 ExtensionsPath(&'a [u8]),
258 IpForwarding(bool),
260 NonLocalSrcRouting(bool),
262 PolicyFilter(&'a [[Addr; 2]]),
264 MaximumDatagramSize(u16),
268 RequestedIpAddress(&'a Addr),
270 AddressLeaseTime(u32),
274 OptionOverload(OptionOverload),
276 MessageType(MessageType),
280 ServerIdentifier(&'a Addr),
282 ParameterRequestList(&'a [u8]),
284 Message(&'a [u8]),
286 MaximumMessageSize(u16),
290 VendorClassIdentifier(&'a [u8]),
292 ClientIdentifier(&'a [u8]),
298 RelayAgentInformation(relay::RelayAgentInformation<'a>),
300 Unknown(u8, &'a [u8]),
302}
303
304#[inline]
305fn read_str(b: &[u8]) -> Result<&[u8], Error> {
306 if b.is_empty() {
307 return Err(Error::Malformed);
308 }
309 Ok(b)
310}
311
312#[inline]
313fn read_addrs(b: &[u8]) -> Result<&[Addr], Error> {
314 if b.len() < 4 || b.len() % mem::size_of::<Addr>() != 0 {
315 return Err(Error::Malformed);
316 }
317 Ok(unsafe {
319 slice::from_raw_parts(
320 b as *const [u8] as *const Addr,
321 b.len() / mem::size_of::<Addr>(),
322 )
323 })
324}
325
326#[inline]
327fn read_addr_pairs(b: &[u8]) -> Result<&[[Addr; 2]], Error> {
328 if b.len() < 8 || b.len() % mem::size_of::<[Addr; 2]>() != 0 {
329 return Err(Error::Malformed);
330 }
331 Ok(unsafe {
333 slice::from_raw_parts(
334 b as *const [u8] as *const [Addr; 2],
335 b.len() / mem::size_of::<[Addr; 2]>(),
336 )
337 })
338}
339
340impl<'a> DhcpOption<'a> {
341 pub fn code(&self) -> u8 {
343 use DhcpOption::*;
344 match *self {
345 Pad => 0,
346 End => 255,
347 SubnetMask(_) => 1,
348 TimeOffset(_) => 2,
349 Router(_) => 3,
350 TimeServer(_) => 4,
351 NameServer(_) => 5,
352 DomainNameServer(_) => 6,
353 LogServer(_) => 7,
354 CookieServer(_) => 8,
355 LprServer(_) => 9,
356 ImpressServer(_) => 10,
357 ResourceLocationServer(_) => 11,
358 HostName(_) => 12,
359 BootFileSize(_) => 13,
360 MeritDumpFile(_) => 14,
361 DomainName(_) => 15,
362 SwapServer(_) => 16,
363 RootPath(_) => 17,
364 ExtensionsPath(_) => 18,
365 IpForwarding(_) => 19,
366 NonLocalSrcRouting(_) => 20,
367 PolicyFilter(_) => 21,
368 MaximumDatagramSize(_) => 22,
369 RequestedIpAddress(_) => 50,
370 AddressLeaseTime(_) => 51,
371 OptionOverload(_) => 52,
372 MessageType(_) => 53,
373 ServerIdentifier(_) => 54,
374 ParameterRequestList(_) => 55,
375 Message(_) => 56,
376 MaximumMessageSize(_) => 57,
377 VendorClassIdentifier(_) => 60,
378 ClientIdentifier(_) => 61,
379 RelayAgentInformation(_) => 82,
380 Unknown(code, _) => code,
381 }
382 }
383
384 fn read(buf: &'a [u8]) -> Result<(Self, usize), Error> {
385 use DhcpOption::*;
386 let (tag, b) = match *buf {
387 [0, ..] => return Ok((Pad, 1)),
388 [255, ..] => return Ok((End, 1)),
389 [tag, len, ref rest @ ..] => (tag, rest.get(..len.into()).ok_or(Error::Malformed)?),
390 _ => return Err(Error::Underflow),
391 };
392 Ok((
393 match tag {
394 0 | 255 => unreachable!(),
395 1 => SubnetMask(b.try_into()?),
396 2 => {
397 if b.len() != 4 {
398 return Err(Error::Malformed);
399 }
400 TimeOffset(NetworkEndian::read_i32(b))
401 }
402 3 => Router(read_addrs(b)?),
403 4 => TimeServer(read_addrs(b)?),
404 5 => NameServer(read_addrs(b)?),
405 6 => DomainNameServer(read_addrs(b)?),
406 7 => LogServer(read_addrs(b)?),
407 8 => CookieServer(read_addrs(b)?),
408 9 => LprServer(read_addrs(b)?),
409 10 => ImpressServer(read_addrs(b)?),
410 11 => ResourceLocationServer(read_addrs(b)?),
411 12 => HostName(read_str(b)?),
412 13 => {
413 if b.len() != 2 {
414 return Err(Error::Malformed);
415 }
416 BootFileSize(NetworkEndian::read_u16(b))
417 }
418 14 => MeritDumpFile(read_str(b)?),
419 15 => DomainName(read_str(b)?),
420 16 => SwapServer(b.try_into()?),
421 17 => RootPath(read_str(b)?),
422 18 => ExtensionsPath(read_str(b)?),
423 19 => match *b {
424 [x] => IpForwarding(x == 1),
425 _ => return Err(Error::Malformed),
426 },
427 20 => match *b {
428 [x] => NonLocalSrcRouting(x == 1),
429 _ => return Err(Error::Malformed),
430 },
431 21 => PolicyFilter(read_addr_pairs(b)?),
432 22 => {
433 if b.len() != 2 {
434 return Err(Error::Malformed);
435 }
436 MaximumDatagramSize(NetworkEndian::read_u16(b))
437 }
438 50 => RequestedIpAddress(b.try_into()?),
439 51 => {
440 if b.len() != 4 {
441 return Err(Error::Malformed);
442 }
443 AddressLeaseTime(NetworkEndian::read_u32(b))
444 }
445 52 => match *b {
446 [x] => {
447 OptionOverload(self::OptionOverload::from_bits(x).ok_or(Error::Malformed)?)
448 }
449 _ => return Err(Error::Malformed),
450 },
451 53 => match *b {
452 [x] => MessageType(x.into()),
453 _ => return Err(Error::Malformed),
454 },
455 54 => ServerIdentifier(b.try_into()?),
456 55 => ParameterRequestList(read_str(b)?),
457 56 => Message(read_str(b)?),
458 57 => {
459 if b.len() != 2 {
460 return Err(Error::Malformed);
461 }
462 MaximumMessageSize(NetworkEndian::read_u16(b))
463 }
464 60 => VendorClassIdentifier(read_str(b)?),
465 61 => {
466 if b.len() < 2 {
467 return Err(Error::Malformed);
468 }
469 ClientIdentifier(read_str(b)?)
470 }
471 82 => RelayAgentInformation(relay::RelayAgentInformation::new(b)?),
472 _ => Unknown(tag, b),
473 },
474 2 + b.len(),
475 ))
476 }
477
478 fn write<'buf>(&self, cursor: &mut Cursor<'buf>) -> Result<(), Error> {
479 use DhcpOption::*;
480 cursor.write_u8(self.code())?;
481 match *self {
482 Pad | End => Ok(()),
483 SubnetMask(addr)
484 | SwapServer(addr)
485 | RequestedIpAddress(addr)
486 | ServerIdentifier(addr) => {
487 cursor.write_u8(addr.0.len() as u8)?;
488 cursor.write(&addr.0)
489 }
490 TimeOffset(i) => {
491 cursor.write_u8(4)?;
492 cursor.write(&i.to_be_bytes())
493 }
494 Router(addrs)
495 | TimeServer(addrs)
496 | NameServer(addrs)
497 | DomainNameServer(addrs)
498 | LogServer(addrs)
499 | CookieServer(addrs)
500 | LprServer(addrs)
501 | ImpressServer(addrs)
502 | ResourceLocationServer(addrs) => {
503 let xs = unsafe {
505 slice::from_raw_parts(
506 addrs as *const [Addr] as *const u8,
507 addrs.len() * mem::size_of::<Addr>(),
508 )
509 };
510 cursor.write_u8(xs.len().try_into().map_err(|_| Error::TooLong)?)?;
511 cursor.write(xs)
512 }
513 HostName(xs)
514 | MeritDumpFile(xs)
515 | DomainName(xs)
516 | RootPath(xs)
517 | ExtensionsPath(xs)
518 | RelayAgentInformation(relay::RelayAgentInformation(xs))
519 | ParameterRequestList(xs)
520 | Message(xs)
521 | VendorClassIdentifier(xs)
522 | ClientIdentifier(xs)
523 | Unknown(_, xs) => {
524 cursor.write_u8(xs.len().try_into().map_err(|_| Error::TooLong)?)?;
525 cursor.write(xs)
526 }
527 BootFileSize(x) | MaximumDatagramSize(x) | MaximumMessageSize(x) => {
528 cursor.write_u8(2)?;
529 cursor.write(&x.to_be_bytes())
530 }
531 IpForwarding(x) | NonLocalSrcRouting(x) => {
532 cursor.write_u8(1)?;
533 cursor.write_u8(if x { 1 } else { 0 })
534 }
535 PolicyFilter(addr_pairs) => {
536 let xs = unsafe {
538 slice::from_raw_parts(
539 addr_pairs as *const [[Addr; 2]] as *const u8,
540 addr_pairs.len() * mem::size_of::<[Addr; 2]>(),
541 )
542 };
543 cursor.write_u8(xs.len().try_into().map_err(|_| Error::TooLong)?)?;
544 cursor.write(xs)
545 }
546 AddressLeaseTime(i) => {
547 cursor.write_u8(4)?;
548 cursor.write(&i.to_be_bytes())
549 }
550 OptionOverload(x) => {
551 cursor.write_u8(1)?;
552 cursor.write_u8(x.bits())
553 }
554 MessageType(self::MessageType(x)) => {
555 cursor.write_u8(1)?;
556 cursor.write_u8(x)
557 }
558 }
559 }
560
561 fn size(&self) -> usize {
563 use DhcpOption::*;
564 if matches!(*self, Pad | End) {
565 return 1;
566 }
567 2 + match *self {
568 Pad | End => unreachable!(),
569 IpForwarding(_) | NonLocalSrcRouting(_) | OptionOverload(_) | MessageType(_) => 1,
570 SubnetMask(addr)
571 | SwapServer(addr)
572 | RequestedIpAddress(addr)
573 | ServerIdentifier(addr) => mem::size_of_val(addr),
574 TimeOffset(_) | AddressLeaseTime(_) => 4,
575 Router(addrs)
576 | TimeServer(addrs)
577 | NameServer(addrs)
578 | DomainNameServer(addrs)
579 | LogServer(addrs)
580 | CookieServer(addrs)
581 | LprServer(addrs)
582 | ImpressServer(addrs)
583 | ResourceLocationServer(addrs) => mem::size_of_val(addrs),
584 HostName(xs)
585 | MeritDumpFile(xs)
586 | DomainName(xs)
587 | RootPath(xs)
588 | ExtensionsPath(xs)
589 | RelayAgentInformation(relay::RelayAgentInformation(xs))
590 | ParameterRequestList(xs)
591 | Message(xs)
592 | VendorClassIdentifier(xs)
593 | ClientIdentifier(xs)
594 | Unknown(_, xs) => mem::size_of_val(xs),
595 BootFileSize(_) | MaximumDatagramSize(_) | MaximumMessageSize(_) => 2,
596 PolicyFilter(addr_pairs) => mem::size_of_val(addr_pairs),
597 }
598 }
599}
600
601const SNAME_FIELD_OFFSET: usize = 44;
602const FILE_FIELD_OFFSET: usize = SNAME_FIELD_OFFSET + 64;
603const OPTIONS_FIELD_OFFSET: usize = FILE_FIELD_OFFSET + 128;
604const MAGIC_COOKIE: [u8; 4] = [99, 130, 83, 99];
605
606#[derive(Clone, Debug)]
613pub struct Options<'a> {
614 b: &'a [u8],
616 overload: OptionOverload,
617 cursor: usize,
619}
620
621impl<'a> Options<'a> {
622 #[inline]
623 fn new(b: &'a [u8]) -> Result<Self, Error> {
624 if b[OPTIONS_FIELD_OFFSET..][..MAGIC_COOKIE.len()] != MAGIC_COOKIE {
625 return Err(Error::Malformed);
626 }
627
628 Ok(Self {
629 b,
630 overload: OptionOverload::empty(),
631 cursor: OPTIONS_FIELD_OFFSET + MAGIC_COOKIE.len(),
632 })
633 }
634}
635
636impl<'a> Iterator for Options<'a> {
637 type Item = Result<(DhcpOption<'a>, (usize, usize)), Error>;
638
639 fn next(&mut self) -> Option<Self::Item> {
640 loop {
641 let b = if self.cursor > OPTIONS_FIELD_OFFSET {
642 &self.b[self.cursor..]
643 } else if self.cursor >= FILE_FIELD_OFFSET {
644 &self.b[FILE_FIELD_OFFSET..][..128][self.cursor - FILE_FIELD_OFFSET..]
645 } else {
646 &self.b[SNAME_FIELD_OFFSET..][..64][self.cursor - SNAME_FIELD_OFFSET..]
647 };
648 match DhcpOption::read(b) {
649 Ok((DhcpOption::Pad, len)) => self.cursor += len,
650 Ok((DhcpOption::End, _)) => {
651 self.cursor = if self.cursor > OPTIONS_FIELD_OFFSET
652 && self.overload.contains(OptionOverload::FILE)
653 {
654 FILE_FIELD_OFFSET } else if self.cursor >= FILE_FIELD_OFFSET
656 && self.overload.contains(OptionOverload::SNAME)
657 {
658 SNAME_FIELD_OFFSET } else {
660 break None;
661 }
662 }
663 Ok((option, len)) => {
664 let bnd = (self.cursor, len);
665 self.cursor += len;
666 if let DhcpOption::OptionOverload(x) = option {
667 self.overload = x;
668 }
669 break Some(Ok((option, bnd)));
670 }
671 Err(e) => break Some(Err(e)),
672 }
673 }
674 }
675}
676
677impl FusedIterator for Options<'_> {}
678
679#[derive(Clone, Debug)]
682pub struct Message<T>(T);
683
684impl<T: AsRef<[u8]>> AsRef<[u8]> for Message<T> {
685 #[inline]
686 fn as_ref(&self) -> &[u8] {
687 self.0.as_ref()
688 }
689}
690
691impl<T: AsMut<[u8]>> AsMut<[u8]> for Message<T> {
692 #[inline]
693 fn as_mut(&mut self) -> &mut [u8] {
694 self.0.as_mut()
695 }
696}
697
698impl<T> Message<T> {
699 #[inline]
701 pub fn into_inner(self) -> T {
702 let Self(inner) = self;
703 inner
704 }
705}
706
707impl Default for Message<[u8; 241]> {
708 fn default() -> Self {
712 let mut buf = [0; 241];
713 buf[OPTIONS_FIELD_OFFSET..][..MAGIC_COOKIE.len()].copy_from_slice(&MAGIC_COOKIE);
714 buf[OPTIONS_FIELD_OFFSET + MAGIC_COOKIE.len()] = DhcpOption::End.code();
715 Self(buf)
716 }
717}
718
719impl<T: AsRef<[u8]>> Message<T> {
720 #[inline]
727 pub fn new(b: T) -> Result<Self, Error> {
728 if b.as_ref().len() < OPTIONS_FIELD_OFFSET + MAGIC_COOKIE.len() + 4 {
729 return Err(Error::Underflow);
730 }
731 Ok(Self(b))
732 }
733
734 #[inline]
736 pub fn op(&self) -> Result<OpCode, Error> {
737 self.as_ref()[0].try_into()
738 }
739
740 #[inline]
742 pub fn hlen(&self) -> u8 {
743 self.as_ref()[2]
744 }
745
746 #[inline]
748 pub fn hops(&self) -> u8 {
749 self.as_ref()[3]
750 }
751
752 #[inline]
754 pub fn xid(&self) -> u32 {
755 NetworkEndian::read_u32(&self.as_ref()[4..])
756 }
757
758 #[inline]
763 pub fn secs(&self) -> u16 {
764 NetworkEndian::read_u16(&self.as_ref()[8..])
765 }
766
767 #[inline]
769 pub fn flags(&self) -> Flags {
770 Flags::from_bits_truncate(NetworkEndian::read_u16(&self.as_ref()[10..]))
771 }
772
773 #[inline]
775 pub fn ciaddr(&self) -> &Addr {
776 self.as_ref()[12..].try_into().unwrap()
777 }
778
779 #[inline]
781 pub fn yiaddr(&self) -> &Addr {
782 self.as_ref()[16..].try_into().unwrap()
783 }
784
785 #[inline]
787 pub fn siaddr(&self) -> &Addr {
788 self.as_ref()[20..].try_into().unwrap()
789 }
790
791 #[inline]
793 pub fn giaddr(&self) -> &Addr {
794 self.as_ref()[24..].try_into().unwrap()
795 }
796
797 #[inline]
804 pub fn chaddr(&self) -> Result<&[u8], Error> {
805 self.as_ref()[28..][..16]
806 .get(..self.hlen() as usize)
807 .ok_or(Error::Malformed)
808 }
809
810 pub fn sname(&self) -> Result<&[u8], Error> {
812 let data = self.as_ref();
813 memchr(0, &data[SNAME_FIELD_OFFSET..][..64])
814 .map(|nul_pos| &data[SNAME_FIELD_OFFSET..][..nul_pos])
815 .ok_or(Error::BadNull)
816 }
817
818 pub fn file(&self) -> Result<&[u8], Error> {
820 let data = self.as_ref();
821 memchr(0, &data[FILE_FIELD_OFFSET..][..128])
822 .map(|nul_pos| &data[FILE_FIELD_OFFSET..][..nul_pos])
823 .ok_or(Error::BadNull)
824 }
825
826 #[inline]
832 pub fn options(&self) -> Result<Options<'_>, Error> {
833 Options::new(self.as_ref())
834 }
835}
836
837impl<T: AsMut<[u8]>> Message<T> {
838 #[inline]
840 pub fn set_op(&mut self, op: OpCode) {
841 self.as_mut()[0] = op as u8;
842 }
843
844 pub fn hops_mut(&mut self) -> &mut u8 {
846 &mut self.as_mut()[3]
847 }
848
849 pub fn set_xid(&mut self, xid: u32) {
851 NetworkEndian::write_u32(&mut self.as_mut()[4..], xid);
852 }
853
854 pub fn set_secs(&mut self, secs: u16) {
856 NetworkEndian::write_u16(&mut self.as_mut()[8..], secs);
857 }
858
859 #[inline]
861 pub fn set_flags(&mut self, flags: Flags) {
862 NetworkEndian::write_u16(&mut self.as_mut()[10..], flags.bits());
863 }
864
865 #[inline]
867 pub fn ciaddr_mut(&mut self) -> &mut Addr {
868 Addr::ref_cast_mut((&mut self.as_mut()[12..][..4]).try_into().unwrap())
869 }
870
871 #[inline]
873 pub fn yiaddr_mut(&mut self) -> &mut Addr {
874 Addr::ref_cast_mut((&mut self.as_mut()[16..][..4]).try_into().unwrap())
875 }
876
877 #[inline]
879 pub fn siaddr_mut(&mut self) -> &mut Addr {
880 Addr::ref_cast_mut((&mut self.as_mut()[20..][..4]).try_into().unwrap())
881 }
882
883 #[inline]
885 pub fn giaddr_mut(&mut self) -> &mut Addr {
886 Addr::ref_cast_mut((&mut self.as_mut()[24..][..4]).try_into().unwrap())
887 }
888
889 #[inline]
898 pub fn set_chaddr(&mut self, chaddr: &[u8]) -> Result<(), Error> {
899 self.as_mut()[28..][..16]
900 .get_mut(..chaddr.len())
901 .ok_or(Error::TooLong)?
902 .copy_from_slice(chaddr);
903 self.as_mut()[2] = chaddr.len() as u8;
904 Ok(())
905 }
906}
907
908#[doc(hidden)]
909pub mod _get_options {
910 #[allow(non_upper_case_globals)]
912 pub const required: () = ();
913}
914
915#[macro_export]
944macro_rules! v4_options {
945 ($msg:expr; $($opt:ident $($required:ident)? ),*) => ('outer: loop {
946 use ::core::{result::Result::*, option::Option::*};
947 let mut count = 0;
948 $(#[allow(non_snake_case)] let mut $opt = None; count += 1;)*
949 for x in match $msg.options() {
950 Ok(x) => x,
951 Err(e) => break Err(e),
952 } {
953 match x {
954 $(Ok(($crate::dhcpv4::DhcpOption::$opt(data), _))
955 if $opt.is_none() => { $opt = Some(data); },)*
956 Ok(_) => continue,
957 Err(e) => break 'outer Err(e),
958 }
959 count -= 1;
960 if count == 0 { break; }
961 }
962 break Ok(($({
963 let x = $opt;
964 $(
965 $crate::dhcpv4::_get_options::$required;
966 let x = if let Some(x) = x {
967 x
968 } else {
969 break Err($crate::Error::MissingRequired);
970 };
971 )?
972 x
973 }),*));
974 })
975}
976
977mod private {
978 pub trait Sealed {}
979
980 impl Sealed for super::Encoder {}
981 impl<Prev, I> Sealed for super::AppendOptions<Prev, I> {}
982 impl<Prev> Sealed for super::SetOption<'_, Prev> {}
983 impl<Prev, F> Sealed for super::FilterOptions<Prev, F> {}
984}
985
986pub trait Encode: private::Sealed {
988 fn encode<'dst, T: AsRef<[u8]>>(
993 mut self,
994 src: &Message<T>,
995 dst: &'dst mut [u8],
996 ) -> Result<Message<&'dst mut [u8]>, Error>
997 where
998 Self: Sized,
999 {
1000 let data = src.as_ref();
1001 dst[..data.len()].copy_from_slice(data);
1002 let mut cursor = Cursor {
1003 buffer: dst,
1004 index: OPTIONS_FIELD_OFFSET + MAGIC_COOKIE.len(),
1005 };
1006
1007 src.options()?.try_for_each(|x| {
1008 let (option, bnd) = x?;
1009 self.write_option(&mut cursor, data, (&option, bnd))
1010 })?;
1011 self.write_new_options(&mut cursor)?;
1012 DhcpOption::End.write(&mut cursor)?;
1013
1014 let len = cursor.index;
1015 Ok(Message::new(&mut dst[..len]).unwrap())
1016 }
1017
1018 #[cfg(feature = "std")]
1020 fn encode_to_owned<T: AsRef<[u8]>>(self, src: &Message<T>) -> Result<Message<Vec<u8>>, Error>
1021 where
1022 Self: Sized,
1023 {
1024 let mut buf = vec![0; src.as_ref().len() + self.max_size_diff()];
1025 let len = self.encode(src, &mut buf)?.as_ref().len();
1026 buf.truncate(len);
1027 Ok(Message::new(buf).unwrap())
1028 }
1029
1030 #[doc(hidden)]
1031 fn write_option<'old, 'new>(
1032 &mut self,
1033 cursor: &mut Cursor<'new>,
1034 src: &'old [u8],
1035 old_option: (&DhcpOption<'old>, (usize, usize)),
1036 ) -> Result<(), Error>;
1037
1038 #[doc(hidden)]
1039 fn write_new_options(self, cursor: &mut Cursor) -> Result<(), Error>;
1040
1041 fn max_size_diff(&self) -> usize;
1044
1045 #[inline]
1047 fn append_options<I>(self, options: I) -> AppendOptions<Self, I>
1048 where
1049 Self: Sized,
1050 {
1051 AppendOptions {
1052 prev: self,
1053 options,
1054 }
1055 }
1056
1057 #[inline]
1065 fn set_option(self, option: DhcpOption<'_>) -> SetOption<'_, Self>
1066 where
1067 Self: Sized,
1068 {
1069 SetOption {
1070 prev: self,
1071 replaced: false,
1072 option,
1073 }
1074 }
1075
1076 #[inline]
1079 fn filter_options<F>(self, f: F) -> FilterOptions<Self, F>
1080 where
1081 FilterOptions<Self, F>: Encode,
1082 Self: Sized,
1083 {
1084 FilterOptions { prev: self, f }
1085 }
1086}
1087
1088#[derive(Debug)]
1090pub struct Encoder;
1091
1092impl Encode for Encoder {
1093 #[inline]
1094 fn write_option<'old, 'new>(
1095 &mut self,
1096 cursor: &mut Cursor<'new>,
1097 src: &'old [u8],
1098 (old_option, (start, len)): (&DhcpOption<'old>, (usize, usize)),
1099 ) -> Result<(), Error> {
1100 if let DhcpOption::OptionOverload(_) = old_option {
1101 return Ok(());
1102 }
1103 if cursor.index == start {
1104 cursor.index += len;
1106 Ok(())
1107 } else {
1108 cursor.write(&src[start..][..len])
1109 }
1110 }
1111
1112 #[inline]
1113 fn write_new_options(self, _cursor: &mut Cursor) -> Result<(), Error> {
1114 Ok(())
1115 }
1116
1117 #[inline]
1118 fn max_size_diff(&self) -> usize {
1119 64 + 128 - 2 - 3
1124 }
1125}
1126
1127#[derive(Debug)]
1129pub struct AppendOptions<Prev, I> {
1130 prev: Prev,
1131 options: I,
1132}
1133
1134impl<'a, Prev: Encode, I> Encode for AppendOptions<Prev, I>
1135where
1136 I: IntoIterator<Item = DhcpOption<'a>> + Clone,
1137{
1138 #[inline]
1139 fn write_option<'old, 'new>(
1140 &mut self,
1141 cursor: &mut Cursor<'new>,
1142 src: &'old [u8],
1143 (old_option, bnd): (&DhcpOption<'old>, (usize, usize)),
1144 ) -> Result<(), Error> {
1145 self.prev.write_option(cursor, src, (old_option, bnd))
1146 }
1147
1148 #[inline]
1149 fn write_new_options(self, cursor: &mut Cursor) -> Result<(), Error> {
1150 self.prev.write_new_options(cursor)?;
1151 self.options
1152 .into_iter()
1153 .try_for_each(|option| option.write(cursor))
1154 }
1155
1156 #[inline]
1157 fn max_size_diff(&self) -> usize {
1158 self.prev.max_size_diff()
1159 + self
1160 .options
1161 .clone()
1162 .into_iter()
1163 .map(|option| option.size())
1164 .sum::<usize>()
1165 }
1166}
1167
1168#[derive(Debug)]
1170pub struct SetOption<'a, Prev> {
1171 prev: Prev,
1172 replaced: bool,
1173 option: DhcpOption<'a>,
1174}
1175
1176impl<'a, Prev: Encode> Encode for SetOption<'a, Prev> {
1177 #[inline]
1178 fn write_option<'old, 'new>(
1179 &mut self,
1180 cursor: &mut Cursor<'new>,
1181 src: &'old [u8],
1182 (old_option, bnd): (&DhcpOption<'old>, (usize, usize)),
1183 ) -> Result<(), Error> {
1184 if old_option.code() == self.option.code() {
1185 if !self.replaced {
1186 self.option.write(cursor)?;
1187 }
1188 self.replaced = true;
1189 Ok(())
1190 } else {
1191 self.prev.write_option(cursor, src, (old_option, bnd))
1192 }
1193 }
1194
1195 #[inline]
1196 fn write_new_options(self, cursor: &mut Cursor) -> Result<(), Error> {
1197 self.prev.write_new_options(cursor)?;
1198 if !self.replaced {
1199 self.option.write(cursor)?;
1200 }
1201 Ok(())
1202 }
1203
1204 #[inline]
1205 fn max_size_diff(&self) -> usize {
1206 self.prev.max_size_diff() + self.option.size()
1207 }
1208}
1209
1210#[derive(Debug)]
1212pub struct FilterOptions<Prev, F> {
1213 prev: Prev,
1214 f: F,
1215}
1216
1217impl<Prev: Encode, F> Encode for FilterOptions<Prev, F>
1218where
1219 for<'a> F: Fn(DhcpOption<'a>) -> bool,
1220{
1221 #[inline]
1222 fn write_option<'old, 'new>(
1223 &mut self,
1224 cursor: &mut Cursor<'new>,
1225 src: &'old [u8],
1226 (old_option, bnd): (&DhcpOption<'old>, (usize, usize)),
1227 ) -> Result<(), Error> {
1228 if (self.f)(*old_option) {
1229 self.prev.write_option(cursor, src, (old_option, bnd))
1230 } else {
1231 Ok(())
1232 }
1233 }
1234
1235 #[inline]
1236 fn write_new_options(self, cursor: &mut Cursor) -> Result<(), Error> {
1237 self.prev.write_new_options(cursor)
1238 }
1239
1240 #[inline]
1241 fn max_size_diff(&self) -> usize {
1242 self.prev.max_size_diff()
1243 }
1244}
1245
1246#[cfg(test)]
1247mod tests {
1248 use super::*;
1249
1250 const EX_MSG: [u8; 244] = [
1252 2, 1, 6, 0,
1253 0xC7, 0xF5, 0xA0, 0xA7, 1, 2, 0x80, 0x00,
1254 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78,
1255 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
1256 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1257 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1258 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1259 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 1, 2, 255, 0,
1260 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1261 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1262 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1263 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1264 0, 0, 0, 99, 130, 83, 99, 52, 1, 0b01, 255,
1266 ];
1267
1268 #[test]
1269 fn it_works() -> Result<(), Error> {
1270 use std::net::Ipv4Addr;
1271
1272 let view = Message::new(EX_MSG)?;
1273 assert_eq!(view.op()?, OpCode::BootReply);
1274 assert_eq!(view.hlen(), 6);
1275 assert_eq!(view.secs(), 0x0102);
1276 assert_eq!(view.flags(), Flags::BROADCAST);
1277 assert_eq!(view.yiaddr(), &Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).into());
1278 assert_eq!(view.siaddr(), &Ipv4Addr::UNSPECIFIED.into());
1279 assert_eq!(view.giaddr(), &Ipv4Addr::new(0x11, 0x22, 0x33, 0x44).into());
1280 assert_eq!(view.chaddr()?, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1281 Ok(())
1282 }
1283
1284 #[test]
1285 fn can_set_giaddr() -> Result<(), Error> {
1286 use std::net::Ipv4Addr;
1287
1288 let mut view = Message::new(EX_MSG)?;
1289 let new_giaddr = Ipv4Addr::new(0x31, 0x41, 0x59, 0x26);
1290 *view.giaddr_mut() = new_giaddr.into();
1291 assert_eq!(view.giaddr(), &new_giaddr.into());
1292 Ok(())
1293 }
1294
1295 #[test]
1296 fn read_options() -> Result<(), Error> {
1297 let view = Message::new(EX_MSG)?;
1298 assert_eq!(
1299 view.options()?.collect::<Result<Vec<_>, _>>()?,
1300 [
1301 (DhcpOption::OptionOverload(OptionOverload::FILE), (240, 3)),
1302 (DhcpOption::MessageType(MessageType::OFFER), (108, 3))
1303 ]
1304 );
1305 Ok(())
1306 }
1307
1308 #[test]
1309 fn write_options() -> Result<(), Error> {
1310 let view = Message::new(EX_MSG)?;
1311 let mut out = [0; MAX_MESSAGE_SIZE];
1312
1313 assert_eq!(
1315 Encoder
1316 .set_option(DhcpOption::MessageType(MessageType::REQUEST))
1317 .encode(&view, &mut out)?
1318 .options()?
1319 .map(|x| x.unwrap().0)
1320 .find(|x| matches!(x, DhcpOption::MessageType(_))),
1321 Some(DhcpOption::MessageType(MessageType::REQUEST)),
1322 );
1323
1324 assert_eq!(
1326 Encoder
1327 .set_option(DhcpOption::IpForwarding(true))
1328 .encode(&view, &mut out)?
1329 .options()?
1330 .map(|x| x.unwrap().0)
1331 .find(|x| matches!(x, DhcpOption::IpForwarding(_))),
1332 Some(DhcpOption::IpForwarding(true)),
1333 );
1334
1335 Ok(())
1336 }
1337
1338 #[test]
1339 fn construct_new_msg() -> Result<(), Error> {
1340 let client_id = [6, 0x06, 0, 0, 0, 0, 0];
1341 let chaddr = &client_id[1..];
1342 let mut msg = Encoder
1343 .append_options([
1344 DhcpOption::MessageType(MessageType::DISCOVER),
1345 DhcpOption::ClientIdentifier(&client_id),
1346 ])
1347 .encode_to_owned(&Message::default())?;
1348 msg.set_chaddr(chaddr)?;
1349
1350 assert_eq!(msg.chaddr()?, chaddr);
1351 assert_eq!(
1352 msg.options()?.collect::<Result<Vec<_>, _>>()?,
1353 [
1354 (DhcpOption::MessageType(MessageType::DISCOVER), (240, 3)),
1355 (DhcpOption::ClientIdentifier(&client_id), (243, 9))
1356 ]
1357 );
1358 Ok(())
1359 }
1360
1361 #[test]
1362 fn option_size_includes_code_and_len() {
1363 assert_eq!(DhcpOption::End.size(), 1);
1364 assert_eq!(DhcpOption::MessageType(MessageType::DISCOVER).size(), 3);
1365 }
1366}