1//! ENDS option to provide a Name Server Identifer.
2//!
3//! The option in this module – [`Nsid<Octs>`] – allows a resolver to query
4//! for and a server to provide an identifier for the particular server that
5//! answered the query. This can be helpful when debugging a scenario where
6//! multiple servers serve a common address.
7//!
8//! The option is defined in [RFC 5001](https://tools.ietf.org/html/rfc5001).
910use super::super::iana::OptionCode;
11use super::super::message_builder::OptBuilder;
12use super::super::wire::{Composer, ParseError};
13use super::{
14 BuildDataError, LongOptData, Opt, OptData, ComposeOptData, ParseOptData
15};
16use octseq::builder::OctetsBuilder;
17use octseq::octets::Octets;
18use octseq::parse::Parser;
19use core::{borrow, fmt, hash, str};
20use core::cmp::Ordering;
212223//------------ Nsid ---------------------------------------------------------/
2425/// Option data for the Name Server Identifier (NSID) Option.
26///
27/// This option allows identifying a particular name server that has answered
28/// a query. If a client is interested in this information, it includes an
29/// empty NSID option in its query. If the server supports the option, it
30/// includes it in its response with byte string identifying the server.
31///
32/// The option and details about its use are defined in
33/// [RFC 5001](https://tools.ietf.org/html/rfc5001).
34#[derive(Clone, Copy)]
35pub struct Nsid<Octs: ?Sized> {
36/// The octets of the identifier.
37octets: Octs,
38}
3940impl<Octs> Nsid<Octs> {
41/// Creates a value from the ocets of the name server identifier.
42 ///
43 /// The function returns an error if `octets` is longer than 65,535
44 /// octets.
45pub fn from_octets(octets: Octs) -> Result<Self, LongOptData>
46where Octs: AsRef<[u8]> {
47 LongOptData::check_len(octets.as_ref().len())?;
48Ok(unsafe { Self::from_octets_unchecked(octets) })
49 }
5051/// Creates a value from the name server identifier without checking.
52 ///
53 /// # Safety
54 ///
55 /// The caller has to make sure that `octets` is no longer than 65,535
56 /// octets.
57pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
58 Nsid { octets }
59 }
6061/// Parses a value from its wire format.
62pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
63 parser: &mut Parser<'a, Src>
64 ) -> Result<Self, ParseError> {
65let len = parser.remaining();
66 LongOptData::check_len(len)?;
67Ok(unsafe { Self::from_octets_unchecked(
68 parser.parse_octets(len)?
69)})
70 }
71}
7273impl Nsid<[u8]> {
74/// Creates a value for a slice of the name server identifer.
75 ///
76 /// The function returns an error if `slice` is longer than 65,535
77 /// octets.
78pub fn from_slice(slice: &[u8]) -> Result<&Self, LongOptData> {
79 LongOptData::check_len(slice.len())?;
80Ok(unsafe { Self::from_slice_unchecked(slice) })
81 }
8283/// Creates a value for a slice without checking.
84 ///
85 /// # Safety
86 ///
87 /// The caller has to make sure that `octets` is no longer than 65,535
88 /// octets.
89#[must_use]
90pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
91&*(slice as *const [u8] as *const Self)
92 }
9394/// Creates an empty NSID option value.
95#[must_use]
96pub fn empty() -> &'static Self {
97unsafe { Self::from_slice_unchecked(b"") }
98 }
99}
100101impl<Octs: ?Sized> Nsid<Octs> {
102/// Returns a reference to the octets with the server identifier.
103pub fn as_octets(&self) -> &Octs {
104&self.octets
105 }
106107/// Converts the value into the octets with the server identifier.
108pub fn into_octets(self) -> Octs
109where
110Octs: Sized,
111 {
112self.octets
113 }
114115/// Returns a slice of the server identifier.
116pub fn as_slice(&self) -> &[u8]
117where
118Octs: AsRef<[u8]>,
119 {
120self.octets.as_ref()
121 }
122123/// Returns a value over an octets slice.
124pub fn for_slice(&self) -> &Nsid<[u8]>
125where
126Octs: AsRef<[u8]>
127 {
128unsafe { Nsid::from_slice_unchecked(self.octets.as_ref()) }
129 }
130}
131132//--- AsRef and Borrow
133134impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for Nsid<Octs> {
135fn as_ref(&self) -> &[u8] {
136self.as_slice()
137 }
138}
139140impl<Octs: AsRef<[u8]> + ?Sized> borrow::Borrow<[u8]> for Nsid<Octs> {
141fn borrow(&self) -> &[u8] {
142self.as_slice()
143 }
144}
145146//--- OptData etc.
147148impl<Octs: ?Sized> OptData for Nsid<Octs> {
149fn code(&self) -> OptionCode {
150 OptionCode::Nsid
151 }
152}
153154impl<'a, Octs: Octets> ParseOptData<'a, Octs> for Nsid<Octs::Range<'a>> {
155fn parse_option(
156 code: OptionCode,
157 parser: &mut Parser<'a, Octs>,
158 ) -> Result<Option<Self>, ParseError> {
159if code == OptionCode::Nsid {
160Self::parse(parser).map(Some)
161 }
162else {
163Ok(None)
164 }
165 }
166}
167168impl<Octs: AsRef<[u8]> + ?Sized> ComposeOptData for Nsid<Octs> {
169fn compose_len(&self) -> u16 {
170self.octets.as_ref().len().try_into().expect("long option data")
171 }
172173fn compose_option<Target: OctetsBuilder + ?Sized>(
174&self, target: &mut Target
175 ) -> Result<(), Target::AppendError> {
176 target.append_slice(self.octets.as_ref())
177 }
178}
179180//--- Display and Debug
181182impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Nsid<Octs> {
183fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184// RFC 5001 § 2.4:
185 // | User interfaces MUST read and write the contents of the NSID
186 // | option as a sequence of hexadecimal digits, two digits per
187 // | payload octet.
188for v in self.octets.as_ref() {
189write!(f, "{:X} ", *v)?;
190 }
191if let Ok(s) = str::from_utf8(self.octets.as_ref()) {
192write!(f, "({})", s)?;
193 }
194Ok(())
195 }
196}
197198impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Nsid<Octs> {
199fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200write!(f, "Nsid({})", self)
201 }
202}
203204//--- PartialEq and Eq
205206impl<Octs, Other> PartialEq<Other> for Nsid<Octs>
207where
208Octs: AsRef<[u8]> + ?Sized,
209 Other: AsRef<[u8]> + ?Sized,
210{
211fn eq(&self, other: &Other) -> bool {
212self.as_slice().eq(other.as_ref())
213 }
214}
215216impl<Octs: AsRef<[u8]> + ?Sized> Eq for Nsid<Octs> { }
217218//--- PartialOrd and Ord
219220impl<Octs, Other> PartialOrd<Other> for Nsid<Octs>
221where
222Octs: AsRef<[u8]> + ?Sized,
223 Other: AsRef<[u8]> + ?Sized,
224{
225fn partial_cmp(&self, other: &Other) -> Option<Ordering> {
226self.as_slice().partial_cmp(other.as_ref())
227 }
228}
229230impl<Octs: AsRef<[u8]> + ?Sized> Ord for Nsid<Octs> {
231fn cmp(&self, other: &Self) -> Ordering {
232self.as_slice().cmp(other.as_slice())
233 }
234}
235236//--- Hash
237238impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Nsid<Octs> {
239fn hash<H: hash::Hasher>(&self, state: &mut H) {
240self.as_slice().hash(state)
241 }
242}
243244//--- Extended Opt and OptBuilder
245246impl<Octs: Octets> Opt<Octs> {
247/// Returns the first NSID option present.
248 ///
249 /// In a response, the NSID option contains an identifier of the name
250 /// server that answered the query. In a query, the option is empty and
251 /// signals a request for inclusion in a response.
252pub fn nsid(&self) -> Option<Nsid<Octs::Range<'_>>> {
253self.first()
254 }
255}
256257impl<'a, Target: Composer> OptBuilder<'a, Target> {
258/// Appends an NSID option with the given server identifier.
259 ///
260 /// The NSID option contains an identifier for the name server that
261 /// processed a query.
262 ///
263 /// In a request, the option can be included to request the server to
264 /// include its server identifier. In this case, the data should be
265 /// empty. You can use [`client_nsid`][Self::client_nsid] to easily
266 /// append this version of the option.
267pub fn nsid(
268&mut self, data: &(impl AsRef<[u8]> + ?Sized)
269 ) -> Result<(), BuildDataError> {
270Ok(self.push(Nsid::from_slice(data.as_ref())?)?)
271 }
272273/// Appends the client version of an NSID option.
274 ///
275 /// If included by a client, the NSID option requests that the server
276 /// returns its name server identifier via the NSID option in a response.
277 /// In this case, the option must be empty. This method creates such an
278 /// empty NSID option.
279pub fn client_nsid(&mut self) -> Result<(), Target::AppendError> {
280self.push(Nsid::empty())
281 }
282}
283284//============ Testing ======================================================
285286#[cfg(test)]
287#[cfg(all(feature = "std", feature = "bytes"))]
288mod test {
289use super::*;
290use super::super::test::test_option_compose_parse;
291292#[test]
293 #[allow(clippy::redundant_closure)] // lifetimes ...
294fn nsid_compose_parse() {
295 test_option_compose_parse(
296&Nsid::from_octets("foo").unwrap(),
297 |parser| Nsid::parse(parser)
298 );
299 }
300}