domain/base/opt/
padding.rs

1//! EDNS options for paddin message sizes.
2//!
3//! The option in this module – [`Padding<Octs>`] – allows to increase the
4//! size of a DNS message to any desired value. This can be helpful with
5//! confidentialty.
6//!
7//! Since this option does not have any meaning for the receiver of a message,
8//! you should generally just use the [`OptBuilder::padding`] and
9//! [`OptBuilder::random_padding`] methods when constructing a message.
10
11use super::super::iana::OptionCode;
12use super::super::message_builder::OptBuilder;
13use super::super::wire::{Compose, Composer, ParseError};
14use super::{ComposeOptData, LongOptData, OptData, ParseOptData};
15use core::{borrow, fmt, str};
16use octseq::builder::OctetsBuilder;
17use octseq::octets::{Octets, OctetsFrom};
18use octseq::parse::Parser;
19
20//------------ Padding -------------------------------------------------------
21
22/// Option data for the padding option.
23///
24/// This option is used to increase the size of a DNS message to a fixed
25/// value so eavesdropper can’t dertermine information from the size.
26///
27/// Generally, you should not need to use this type. Instead, you can use
28/// the [`OptBuilder::padding`] and [`OptBuilder::random_padding`] methods to
29/// add padding to a message – and ignore it when receving one.
30///
31/// The option is defined in [RFC 7830](https://tools.ietf.org/html/rfc7830).
32#[derive(Clone, Copy)]
33pub struct Padding<Octs: ?Sized> {
34    /// The padding octets.
35    octets: Octs,
36}
37
38impl Padding<()> {
39    /// The option code for this option.
40    pub const CODE: OptionCode = OptionCode::PADDING;
41}
42
43impl<Octs> Padding<Octs> {
44    /// Creates a value from the padding octets.
45    ///
46    /// Returns an error if `octets` are longer than 65,535 octets.
47    pub fn from_octets(octets: Octs) -> Result<Self, LongOptData>
48    where
49        Octs: AsRef<[u8]>,
50    {
51        LongOptData::check_len(octets.as_ref().len())?;
52        Ok(unsafe { Self::from_octets_unchecked(octets) })
53    }
54
55    /// Creates a value from the padding octets without checking.
56    ///
57    /// # Safety
58    ///
59    /// The caller needs to ensure that `octets` are not longer than
60    /// 65,535 octets.
61    pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
62        Self { octets }
63    }
64
65    /// Parses a value from its wire formal.
66    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
67        parser: &mut Parser<'a, Src>,
68    ) -> Result<Self, ParseError> {
69        let len = parser.remaining();
70        LongOptData::check_len(len)?;
71        Ok(unsafe { Self::from_octets_unchecked(parser.parse_octets(len)?) })
72    }
73}
74
75impl<Octs: ?Sized> Padding<Octs> {
76    /// Returns a reference to the padding octets.
77    pub fn as_octets(&self) -> &Octs {
78        &self.octets
79    }
80
81    /// Converts the value into the padding octets.
82    pub fn into_octets(self) -> Octs
83    where
84        Octs: Sized,
85    {
86        self.octets
87    }
88
89    /// Returns a slice of the padding octets.
90    pub fn as_slice(&self) -> &[u8]
91    where
92        Octs: AsRef<[u8]>,
93    {
94        self.octets.as_ref()
95    }
96}
97
98//--- OctetsFrom
99
100impl<Octs, SrcOcts> OctetsFrom<Padding<SrcOcts>> for Padding<Octs>
101where
102    Octs: OctetsFrom<SrcOcts>,
103{
104    type Error = Octs::Error;
105
106    fn try_octets_from(src: Padding<SrcOcts>) -> Result<Self, Self::Error> {
107        Octs::try_octets_from(src.octets)
108            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
109    }
110}
111
112//--- AsRef and Borrow
113
114impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for Padding<Octs> {
115    fn as_ref(&self) -> &[u8] {
116        self.as_slice()
117    }
118}
119
120impl<Octs: AsRef<[u8]> + ?Sized> borrow::Borrow<[u8]> for Padding<Octs> {
121    fn borrow(&self) -> &[u8] {
122        self.as_slice()
123    }
124}
125
126//--- OptData
127
128impl<Octs> OptData for Padding<Octs> {
129    fn code(&self) -> OptionCode {
130        OptionCode::PADDING
131    }
132}
133
134impl<'a, Octs: Octets> ParseOptData<'a, Octs> for Padding<Octs::Range<'a>> {
135    fn parse_option(
136        code: OptionCode,
137        parser: &mut Parser<'a, Octs>,
138    ) -> Result<Option<Self>, ParseError> {
139        if code == OptionCode::PADDING {
140            Self::parse(parser).map(Some)
141        } else {
142            Ok(None)
143        }
144    }
145}
146
147impl<Octs: AsRef<[u8]>> ComposeOptData for Padding<Octs> {
148    fn compose_len(&self) -> u16 {
149        self.octets
150            .as_ref()
151            .len()
152            .try_into()
153            .expect("long option data")
154    }
155
156    fn compose_option<Target: OctetsBuilder + ?Sized>(
157        &self,
158        target: &mut Target,
159    ) -> Result<(), Target::AppendError> {
160        target.append_slice(self.octets.as_ref())
161    }
162}
163
164//--- Display and Debug
165
166impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Padding<Octs> {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        for v in self.octets.as_ref() {
169            write!(f, "{:X} ", *v)?;
170        }
171        if let Ok(s) = str::from_utf8(self.octets.as_ref()) {
172            write!(f, "({})", s)?;
173        }
174        Ok(())
175    }
176}
177
178impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Padding<Octs> {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        write!(f, "Padding({})", self)
181    }
182}
183
184//--- Extended OptBuilder
185
186impl<Target: Composer> OptBuilder<'_, Target> {
187    pub fn padding(&mut self, len: u16) -> Result<(), Target::AppendError> {
188        self.push_raw_option(OptionCode::PADDING, len, |target| {
189            for _ in 0..len {
190                0u8.compose(target)?
191            }
192            Ok(())
193        })
194    }
195
196    #[cfg(feature = "rand")]
197    pub fn random_padding(
198        &mut self,
199        len: u16,
200    ) -> Result<(), Target::AppendError> {
201        self.push_raw_option(OptionCode::PADDING, len, |target| {
202            for _ in 0..len {
203                rand::random::<u8>().compose(target)?
204            }
205            Ok(())
206        })
207    }
208}
209
210//--- Serialize
211
212#[cfg(feature = "serde")]
213impl<Octs: AsRef<[u8]>> serde::Serialize for Padding<Octs> {
214    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215    where
216        S: serde::Serializer,
217    {
218        use octseq::serde::SerializeOctets;
219        self.octets.as_ref().serialize_octets(serializer)
220    }
221}