domain/base/iana/
rcode.rs

1//! DNS response codes and extended response codes.
2//!
3//! The original DNS specification in [RFC 1035] specified four bits of the
4//! message header as response code. The type [`Rcode`] defined herein
5//! represents these codes. Later, [RFC 2671][] (now [RFC 6891]) added eight
6//! bits to the response code to be transmitted as part of the OPT
7//! pseudo-resource record. To make matters even worse, the TSIG and TKEY
8//! records defined by [RFC 2845] and [RFC 2930] use a 16 bit error code.
9//! All of these codes share the same definition space. Even so, we have
10//! separate types for each of these.
11//!
12//! [RFC 1035]: https://tools.ietf.org/html/rfc1035
13//! [RFC 2671]: https://tools.ietf.org/html/rfc2671
14//! [RFC 2845]: https://tools.ietf.org/html/rfc2845
15//! [RFC 2930]: https://tools.ietf.org/html/rfc2930
16//! [RFC 6891]: https://tools.ietf.org/html/rfc6891
17
18//  Note: Rcode and OptRcode don’t use the macros since they don’t use all the
19//  bits of the wrapped integer.
20
21use core::fmt;
22use core::str::FromStr;
23
24//------------ Rcode ---------------------------------------------------------
25
26/// DNS Response Codes.
27///
28/// The response code of a response indicates what happend on the server
29/// when trying to answer the query. The code is a 4 bit value and part of
30/// the header of a DNS message.
31///
32/// This response was defined as part of [RFC 1035]. Later, [RFC 2671]
33/// defined an extended response code of 12 bits using the lower four bits
34/// from the header and eight additional bits stored in the OPT
35/// pseudo-record. The type [OptRcode] represents this extended response
36/// code. A third response code, now 16 bit wide, was defined for the
37/// transaction authentication mechansim (TSIG) in [RFC 2845] and is
38/// represented by [TsigRcode].
39///
40/// All three codes share the same name space. Their values are defined in
41/// one registry, [IANA DNS RCODEs]. This type is complete as of 2019-01-28.
42///
43/// [IANA DNS RCODEs]: http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
44/// [RFC 1035]: https://tools.ietf.org/html/rfc1035
45/// [RFC 2671]: https://tools.ietf.org/html/rfc2671
46#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
47pub struct Rcode(u8);
48
49impl Rcode {
50    /// No error condition.
51    ///
52    /// (Otherwise known as success.)
53    ///
54    /// Defined in [RFC 1035].
55    ///
56    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
57    pub const NOERROR: Self = Self(0);
58
59    /// Format error.
60    ///
61    /// The name server was unable to interpret the query.
62    ///
63    /// Defined in [RFC 1035].
64    ///
65    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
66    pub const FORMERR: Self = Self(1);
67
68    /// Server failure.
69    ///
70    /// The name server was unable to process this query due to a problem
71    /// with the name server.
72    ///
73    /// Defined in [RFC 1035].
74    ///
75    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
76    pub const SERVFAIL: Self = Self(2);
77
78    /// Name error.
79    ///
80    /// The domain name given in the query does not exist at the name server.
81    ///
82    /// Defined in [RFC 1035].
83    ///
84    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
85    pub const NXDOMAIN: Self = Self(3);
86
87    /// Not implemented.
88    ///
89    /// The name server does not support the requested kind of query.
90    ///
91    /// Defined in [RFC 1035].
92    ///
93    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
94    pub const NOTIMP: Self = Self(4);
95
96    /// Query refused.
97    ///
98    /// The name server refused to perform the operation requested by the
99    /// query for policy reasons.
100    ///
101    /// Defined in [RFC 1035].
102    ///
103    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
104    pub const REFUSED: Self = Self(5);
105
106    /// Name exists when it should not.
107    ///
108    /// Returned for an UPDATE query when a domain requested to not exist
109    /// does in fact exist.
110    ///
111    /// Returned when resolving a DNAME redirection when the resulting name
112    /// exceeds the length of 255 octets.
113    ///
114    /// Defined in [RFC 2136] for the UPDATE query and [RFC 6672] for DNAME
115    /// redirection.
116    ///
117    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
118    /// [RFC 6672]: https://tools.ietf.org/html/rfc6672
119    pub const YXDOMAIN: Self = Self(6);
120
121    /// RR set exists when it should not.
122    ///
123    /// Returned for an UPDATE query when an RRset requested to not exist
124    /// does in fact exist.
125    ///
126    /// Defined in [RFC 2136].
127    ///
128    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
129    pub const YXRRSET: Self = Self(7);
130
131    /// RR set that should exist does not.
132    ///
133    /// Returned for an UPDATE query when an RRset requested to exist
134    /// does not.
135    ///
136    /// Defined in [RFC 2136].
137    ///
138    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
139    pub const NXRRSET: Self = Self(8);
140
141    /// Server not authoritative for zone or client not authorized.
142    ///
143    /// Returned for an UPDATE query when the server is not an authoritative
144    /// name server for the requested domain.
145    ///
146    /// Returned for queries using TSIG when authorisation failed.
147    ///
148    /// Defined in [RFC 2136] for UPDATE and [RFC 2845] for TSIG.
149    ///
150    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
151    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
152    pub const NOTAUTH: Self = Self(9);
153
154    /// Name not contained in zone.
155    ///
156    /// A name used in the prerequisite or update section is not within the
157    /// zone given in the zone section.
158    ///
159    /// Defined in [RFC 2136].
160    ///
161    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
162    pub const NOTZONE: Self = Self(10);
163}
164
165impl Rcode {
166    /// Creates an rcode from an integer, returning `None` if invalid.
167    ///
168    /// The rcode is valid if the upper four bits of `value` are all zero.
169    #[must_use]
170    pub const fn checked_from_int(value: u8) -> Option<Self> {
171        if value & 0xF0 != 0 {
172            None
173        } else {
174            Some(Rcode(value))
175        }
176    }
177
178    /// Creates an rcode from an integer, only considering the lower four bits.
179    ///
180    /// This function will ignore the upper four bit of `value`.
181    #[must_use]
182    pub const fn masked_from_int(value: u8) -> Self {
183        Rcode(value & 0x0F)
184    }
185
186    /// Returns the integer value for this rcode.
187    ///
188    /// Only the lower 4 bits of the returned octet are used by the rcode. The
189    /// upper four bits are always zero.
190    #[must_use]
191    pub const fn to_int(self) -> u8 {
192        self.0
193    }
194
195    /// Returns the mnemonic for this value if there is one.
196    #[must_use]
197    pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
198        match self {
199            Rcode::NOERROR => Some(b"NOERROR"),
200            Rcode::FORMERR => Some(b"FORMERR"),
201            Rcode::SERVFAIL => Some(b"SERVFAIL"),
202            Rcode::NXDOMAIN => Some(b"NXDOMAIN"),
203            Rcode::NOTIMP => Some(b"NOTIMP"),
204            Rcode::REFUSED => Some(b"REFUSED"),
205            Rcode::YXDOMAIN => Some(b"YXDOMAIN"),
206            Rcode::YXRRSET => Some(b"YXRRSET"),
207            Rcode::NXRRSET => Some(b"NXRRSET"),
208            Rcode::NOTAUTH => Some(b"NOTAUTH"),
209            Rcode::NOTZONE => Some(b"NOTZONE"),
210            _ => None,
211        }
212    }
213}
214
215//--- FromStr
216
217impl FromStr for Rcode {
218    type Err = ();
219
220    fn from_str(s: &str) -> Result<Self, Self::Err> {
221        match s {
222            "NOERROR" => Ok(Rcode::NOERROR),
223            "FORMERR" => Ok(Rcode::FORMERR),
224            "SERVFAIL" => Ok(Rcode::SERVFAIL),
225            "NXDOMAIN" => Ok(Rcode::NXDOMAIN),
226            "NOTIMP" => Ok(Rcode::NOTIMP),
227            "REFUSED" => Ok(Rcode::REFUSED),
228            "YXDOMAIN" => Ok(Rcode::YXDOMAIN),
229            "YXRRSET" => Ok(Rcode::YXRRSET),
230            "NXRRSET" => Ok(Rcode::NXRRSET),
231            "NOTAUTH" => Ok(Rcode::NOTAUTH),
232            "NOTZONE" => Ok(Rcode::NOTZONE),
233            _ => Err(()),
234        }
235    }
236}
237
238//--- TryFrom and From
239
240impl TryFrom<u8> for Rcode {
241    type Error = InvalidRcode;
242
243    fn try_from(value: u8) -> Result<Self, Self::Error> {
244        Rcode::checked_from_int(value).ok_or(InvalidRcode(()))
245    }
246}
247
248impl From<Rcode> for u8 {
249    fn from(value: Rcode) -> u8 {
250        value.to_int()
251    }
252}
253
254//--- Display and Debug
255
256impl fmt::Display for Rcode {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        match self
259            .to_mnemonic()
260            .and_then(|bytes| core::str::from_utf8(bytes).ok())
261        {
262            Some(mnemonic) => f.write_str(mnemonic),
263            None => self.0.fmt(f),
264        }
265    }
266}
267
268impl fmt::Debug for Rcode {
269    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270        match self
271            .to_mnemonic()
272            .and_then(|bytes| core::str::from_utf8(bytes).ok())
273        {
274            Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
275            None => f.debug_tuple("Rcode").field(&self.0).finish(),
276        }
277    }
278}
279
280//--- Serialize and Deserialize
281
282#[cfg(feature = "serde")]
283impl serde::Serialize for Rcode {
284    fn serialize<S: serde::Serializer>(
285        &self,
286        serializer: S,
287    ) -> Result<S::Ok, S::Error> {
288        self.to_int().serialize(serializer)
289    }
290}
291
292#[cfg(feature = "serde")]
293impl<'de> serde::Deserialize<'de> for Rcode {
294    fn deserialize<D: serde::Deserializer<'de>>(
295        deserializer: D,
296    ) -> Result<Self, D::Error> {
297        u8::deserialize(deserializer).and_then(|code| {
298            Rcode::try_from(code).map_err(serde::de::Error::custom)
299        })
300    }
301}
302
303//------------ OptRcode -----------------------------------------------------
304
305/// Extended DNS Response Codes for OPT records.
306///
307/// Originally, the response code embedded in the header of each DNS
308/// message was four bits long. This code, defined in [RFC 1035], is
309/// represented by the [Rcode] type. The extension mechanism for DNS
310/// initially defined in [RFC 2671] and updated by [RFC 6891] added eight
311/// more bits to be stored in the OPT pseudo-resource record. This type
312/// represents the complete 12 bit extended response code.
313///
314/// There is a third, 16 bit wide response code for transaction
315/// authentication (TSIG) defined in [RFC 2845] and represented by the
316/// [`TsigRcode`] type. The code mostly shares the same name space except
317/// for an unfortunate collision in between the BADVERS and BADSIG values.
318/// Because of this, we decided to have separate types.
319///
320/// The values for all three response code types are defined in
321/// the [IANA DNS RCODEs] registry. This type is complete as of 2019-01-28.
322///
323/// The 12-bit extended RCODE defined by [RFC 6891] stores the lowest 4-bits
324/// of the extended RCODE in the main DNS header RCODE field and stores the
325/// remaining 8-bits (right shifted by 4-bits) in the OPT record header RCODE
326/// field, like so:
327///
328/// ```text
329/// NoError:    0 = 0b0000_0000_0000
330///                             ^^^^ Stored in DNS header RCODE field
331///                   ^^^^_^^^^      Stored in OPT header RCODE field
332///
333/// FormErr:    1 = 0b0000_0000_0001
334///                             ^^^^ Stored in DNS header RCODE field
335///                   ^^^^_^^^^      Stored in OPT header RCODE field
336///
337/// BadVers:   16 = 0b0000_0001_0000
338///                             ^^^^ Stored in DNS header RCODE field
339///                   ^^^^_^^^^      Stored in OPT header RCODE field
340///
341/// BadCookie: 23 = 0b0000_0001_0111
342///                             ^^^^ Stored in DNS header RCODE field
343///                   ^^^^_^^^^      Stored in OPT header RCODE field
344/// ```
345///
346/// This type offers several functions to ease working with the separate parts
347/// and the combined value of an extended RCODE:
348///
349/// - [`OptRcode::rcode`]: the RFC 1035 header RCODE part.
350/// - [`OptRcode::ext`]`: the RFC 6891 ENDS OPT extended RCODE part.
351/// - [`OptRcode::to_parts`]`: to access both parts at once.
352/// - [`OptRcode::to_int`]`: the IANA number for the RCODE combining both
353///   parts.
354///
355/// [IANA DNS RCODEs]: http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
356/// [RFC 2671]: https://tools.ietf.org/html/rfc2671
357/// [RFC 2845]: https://tools.ietf.org/html/rfc2845
358/// [RFC 2930]: https://tools.ietf.org/html/rfc2930
359/// [RFC 6891]: https://tools.ietf.org/html/rfc6891
360#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
361pub struct OptRcode(u16);
362
363impl OptRcode {
364    /// No error condition.
365    ///
366    /// (Otherwise known as success.)
367    ///
368    /// Defined in [RFC 1035].
369    ///
370    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
371    pub const NOERROR: Self = Self::from_rcode(Rcode::NOERROR);
372
373    /// Format error.
374    ///
375    /// The name server was unable to interpret the query.
376    ///
377    /// Defined in [RFC 1035].
378    ///
379    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
380    pub const FORMERR: Self = Self::from_rcode(Rcode::FORMERR);
381
382    /// Server failure.
383    ///
384    /// The name server was unable to process this query due to a problem
385    /// with the name server.
386    ///
387    /// Defined in [RFC 1035].
388    ///
389    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
390    pub const SERVFAIL: Self = Self::from_rcode(Rcode::SERVFAIL);
391
392    /// Name error.
393    ///
394    /// The domain name given in the query does not exist at the name server.
395    ///
396    /// Defined in [RFC 1035].
397    ///
398    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
399    pub const NXDOMAIN: Self = Self::from_rcode(Rcode::NXDOMAIN);
400
401    /// Not implemented.
402    ///
403    /// The name server does not support the requested kind of query.
404    ///
405    /// Defined in [RFC 1035].
406    ///
407    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
408    pub const NOTIMP: Self = Self::from_rcode(Rcode::NOTIMP);
409
410    /// Query refused.
411    ///
412    /// The name server refused to perform the operation requested by the
413    /// query for policy reasons.
414    ///
415    /// Defined in [RFC 1035].
416    ///
417    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
418    pub const REFUSED: Self = Self::from_rcode(Rcode::REFUSED);
419
420    /// Name exists when it should not.
421    ///
422    /// Returned for an UPDATE query when a domain requested to not exist
423    /// does in fact exist.
424    ///
425    /// Returned when resolving a DNAME redirection when the resulting name
426    /// exceeds the length of 255 octets.
427    ///
428    /// Defined in [RFC 2136] for the UPDATE query and [RFC 6672] for DNAME
429    /// redirection.
430    ///
431    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
432    /// [RFC 6672]: https://tools.ietf.org/html/rfc6672
433    pub const YXDOMAIN: Self = Self::from_rcode(Rcode::YXDOMAIN);
434
435    /// RR set exists when it should not.
436    ///
437    /// Returned for an UPDATE query when an RRset requested to not exist
438    /// does in fact exist.
439    ///
440    /// Defined in [RFC 2136].
441    ///
442    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
443    pub const YXRRSET: Self = Self::from_rcode(Rcode::YXRRSET);
444
445    /// RR set that should exist does not.
446    ///
447    /// Returned for an UPDATE query when an RRset requested to exist
448    /// does not.
449    ///
450    /// Defined in [RFC 2136].
451    ///
452    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
453    pub const NXRRSET: Self = Self::from_rcode(Rcode::NXRRSET);
454
455    /// Server not authoritative for zone or client not authorized.
456    ///
457    /// Returned for an UPDATE query when the server is not an authoritative
458    /// name server for the requested domain.
459    ///
460    /// Returned for queries using TSIG when authorisation failed.
461    ///
462    /// Defined in [RFC 2136] for UPDATE and [RFC 2845] for TSIG.
463    ///
464    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
465    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
466    pub const NOTAUTH: Self = Self::from_rcode(Rcode::NOTAUTH);
467
468    /// Name not contained in zone.
469    ///
470    /// A name used in the prerequisite or update section is not within the
471    /// zone given in the zone section.
472    ///
473    /// Defined in [RFC 2136].
474    ///
475    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
476    pub const NOTZONE: Self = Self::from_rcode(Rcode::NOTZONE);
477
478    /// Bad OPT version.
479    ///
480    /// A name server does not implement the EDNS version requested in the
481    /// OPT record.
482    ///
483    /// Defined in [RFC 6891].
484    ///
485    /// [RFC 6891]: https://tools.ietf.org/html/rfc6891
486    pub const BADVERS: Self = Self(16);
487
488    // XXX We will not define the values from the TSIG and TKEY RFCs,
489    //     unless they are used in OPT records, too?
490    /// Bad or missing server cookie.
491    ///
492    /// The request contained a COOKIE option either without a server cookie
493    /// or with a server cookie that did not validate.
494    ///
495    /// Defined in [RFC 7873].
496    ///
497    /// [RFC 7873]: https://tools.ietf.org/html/rfc7873
498    pub const BADCOOKIE: Self = Self(23);
499}
500
501impl OptRcode {
502    /// Creates an rcode from an integer, returning `None` if invalid.
503    ///
504    /// The rcode is valid if the upper four bits of `value` are all zero.
505    #[must_use]
506    pub const fn checked_from_int(value: u16) -> Option<OptRcode> {
507        if value & 0x0FFF != 0 {
508            None
509        } else {
510            Some(Self(value))
511        }
512    }
513
514    /// Creates an rcode from an integer, only considering the lower four bits.
515    ///
516    /// This function will ignore the upper four bit of `value`.
517    #[must_use]
518    pub const fn masked_from_int(value: u16) -> OptRcode {
519        Self(value & 0x0FFF)
520    }
521
522    /// Creates an OPT rcode from a plain rcode.
523    #[must_use]
524    pub const fn from_rcode(rcode: Rcode) -> Self {
525        Self(rcode.0 as u16)
526    }
527
528    /// Returns the integer value for this rcode.
529    ///
530    /// Only the lower 12 bits of the returned octet are used by the rcode.
531    /// The upper four bits are always zero.
532    #[must_use]
533    pub const fn to_int(self) -> u16 {
534        self.0
535    }
536
537    /// Creates an extended rcode value from its parts.
538    #[must_use]
539    pub fn from_parts(rcode: Rcode, ext: u8) -> OptRcode {
540        OptRcode((u16::from(ext) << 4) | u16::from(rcode.to_int()))
541    }
542
543    /// Returns the two parts of an extended rcode value.
544    #[must_use]
545    pub fn to_parts(self) -> (Rcode, u8) {
546        (Rcode::masked_from_int(self.0 as u8), (self.0 >> 4) as u8)
547    }
548
549    /// Returns the rcode part of the extended rcode.
550    #[must_use]
551    pub fn rcode(self) -> Rcode {
552        self.to_parts().0
553    }
554
555    /// Returns the extended octet of the extended rcode.
556    #[must_use]
557    pub fn ext(self) -> u8 {
558        self.to_parts().1
559    }
560
561    /// Returns true if the RCODE is extended, false otherwise.
562    #[must_use]
563    pub fn is_ext(&self) -> bool {
564        // https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.3
565        // 6.1.3. OPT Record TTL Field Use
566        //   ...
567        //   "EXTENDED-RCODE
568        //       Forms the upper 8 bits of extended 12-bit RCODE (together
569        //       with the 4 bits defined in [RFC1035].  Note that
570        //       EXTENDED-RCODE value 0 indicates that an unextended RCODE is
571        //       in use (values 0 through 15)."
572        self.0 >> 4 != 0
573    }
574
575    /// Returns the mnemonic for this value if there is one.
576    #[must_use]
577    pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
578        match self {
579            OptRcode::NOERROR => Some(b"NOERROR"),
580            OptRcode::FORMERR => Some(b"FORMERR"),
581            OptRcode::SERVFAIL => Some(b"SERVFAIL"),
582            OptRcode::NXDOMAIN => Some(b"NXDOMAIN"),
583            OptRcode::NOTIMP => Some(b"NOTIMP"),
584            OptRcode::REFUSED => Some(b"REFUSED"),
585            OptRcode::YXDOMAIN => Some(b"YXDOMAIN"),
586            OptRcode::YXRRSET => Some(b"YXRRSET"),
587            OptRcode::NXRRSET => Some(b"NXRRSET"),
588            OptRcode::NOTAUTH => Some(b"NOTAUTH"),
589            OptRcode::NOTZONE => Some(b"NOTZONE"),
590            OptRcode::BADVERS => Some(b"BADVERS"),
591            OptRcode::BADCOOKIE => Some(b"BADCOOKIE"),
592            _ => None,
593        }
594    }
595}
596
597//--- FromStr
598
599impl FromStr for OptRcode {
600    type Err = ();
601
602    fn from_str(s: &str) -> Result<Self, Self::Err> {
603        match s {
604            "NOERROR" => Ok(OptRcode::NOERROR),
605            "FORMERR" => Ok(OptRcode::FORMERR),
606            "SERVFAIL" => Ok(OptRcode::SERVFAIL),
607            "NXDOMAIN" => Ok(OptRcode::NXDOMAIN),
608            "NOTIMP" => Ok(OptRcode::NOTIMP),
609            "REFUSED" => Ok(OptRcode::REFUSED),
610            "YXDOMAIN" => Ok(OptRcode::YXDOMAIN),
611            "YXRRSET" => Ok(OptRcode::YXRRSET),
612            "NXRRSET" => Ok(OptRcode::NXRRSET),
613            "NOTAUTH" => Ok(OptRcode::NOTAUTH),
614            "NOTZONE" => Ok(OptRcode::NOTZONE),
615            "BADVERS" => Ok(OptRcode::BADVERS),
616            "BADCOOKIE" => Ok(OptRcode::BADCOOKIE),
617            _ => Err(()),
618        }
619    }
620}
621
622//--- TryFrom and From
623
624impl TryFrom<u16> for OptRcode {
625    type Error = InvalidRcode;
626
627    fn try_from(value: u16) -> Result<Self, Self::Error> {
628        OptRcode::checked_from_int(value).ok_or(InvalidRcode(()))
629    }
630}
631
632impl From<OptRcode> for u16 {
633    fn from(value: OptRcode) -> u16 {
634        value.to_int()
635    }
636}
637
638impl From<Rcode> for OptRcode {
639    fn from(value: Rcode) -> OptRcode {
640        OptRcode::from_rcode(value)
641    }
642}
643
644//--- Display and Debug
645
646impl fmt::Display for OptRcode {
647    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
648        match self
649            .to_mnemonic()
650            .and_then(|bytes| core::str::from_utf8(bytes).ok())
651        {
652            Some(mnemonic) => f.write_str(mnemonic),
653            None => self.0.fmt(f),
654        }
655    }
656}
657
658impl fmt::Debug for OptRcode {
659    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
660        match self
661            .to_mnemonic()
662            .and_then(|bytes| core::str::from_utf8(bytes).ok())
663        {
664            Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
665            None => f.debug_tuple("Rcode").field(&self.0).finish(),
666        }
667    }
668}
669
670//------------ TsigRcode ----------------------------------------------------
671
672int_enum! {
673    /// Response codes for transaction authentication (TSIG).
674    ///
675    /// TSIG and TKEY resource records contain a 16 bit wide error field whose
676    /// values are an extension of the standard DNS [`Rcode`]. While it was
677    /// intended to also share the same space with the extended response codes
678    /// used by EDNS (see [`OptRcode`]), both used the value 16. To allow
679    /// distinguish between the two uses of this value, we have two separate
680    /// types.
681    ///
682    /// The values for all three response code types are defined in
683    /// the [IANA DNS RCODEs] registry. This type is complete as of 2019-01-28.
684    ///
685    /// [`Rcode`]: enum.Rcode.html
686    /// [`OptRcode`]: enum.OptRcode.html
687    /// [IANA DNS RCODEs]: http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
688    =>
689    TsigRcode, u16;
690
691    /// No error condition.
692    ///
693    /// (Otherwise known as success.)
694    ///
695    /// Defined in [RFC 1035].
696    ///
697    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
698    (NOERROR => 0, "NOERROR")
699
700    /// Format error.
701    ///
702    /// The name server was unable to interpret the query.
703    ///
704    /// Defined in [RFC 1035].
705    ///
706    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
707    (FORMERR => 1, "FORMERR")
708
709    /// Server failure.
710    ///
711    /// The name server was unable to process this query due to a problem
712    /// with the name server.
713    ///
714    /// Defined in [RFC 1035].
715    ///
716    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
717    (SERVFAIL => 2, "SERVFAIL")
718
719    /// Name error.
720    ///
721    /// The domain name given in the query does not exist at the name server.
722    ///
723    /// Defined in [RFC 1035].
724    ///
725    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
726    (NXDOMAIN => 3, "NXDOMAIN")
727
728    /// Not implemented.
729    ///
730    /// The name server does not support the requested kind of query.
731    ///
732    /// Defined in [RFC 1035].
733    ///
734    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
735    (NOTIMP => 4, "NOTIMPL")
736
737    /// Query refused.
738    ///
739    /// The name server refused to perform the operation requested by the
740    /// query for policy reasons.
741    ///
742    /// Defined in [RFC 1035].
743    ///
744    /// [RFC 1035]: https://tools.ietf.org/html/rfc1035
745    (REFUSED => 5, "REFUSED")
746
747    /// Name exists when it should not.
748    ///
749    /// Returned for an UPDATE query when a domain requested to not exist
750    /// does in fact exist.
751    ///
752    /// Returned when resolving a DNAME redirection when the resulting name
753    /// exceeds the length of 255 octets.
754    ///
755    /// Defined in [RFC 2136] for the UPDATE query and [RFC 6672] for DNAME
756    /// redirection.
757    ///
758    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
759    /// [RFC 6672]: https://tools.ietf.org/html/rfc6672
760    (YXDOMAIN => 6, "YXDOMAIN")
761
762    /// RR set exists when it should not.
763    ///
764    /// Returned for an UPDATE query when an RRset requested to not exist
765    /// does in fact exist.
766    ///
767    /// Defined in [RFC 2136].
768    ///
769    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
770    (YXRRSET => 7, "YXRRSET")
771
772    /// RR set that should exist does not.
773    ///
774    /// Returned for an UPDATE query when an RRset requested to exist
775    /// does not.
776    ///
777    /// Defined in [RFC 2136].
778    ///
779    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
780    (NXRRSET => 8, "NXRRSET")
781
782    /// Server not authoritative for zone or client not authorized.
783    ///
784    /// Returned for an UPDATE query when the server is not an authoritative
785    /// name server for the requested domain.
786    ///
787    /// Returned for queries using TSIG when authorisation failed.
788    ///
789    /// Defined in [RFC 2136] for UPDATE and [RFC 2845] for TSIG.
790    ///
791    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
792    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
793    (NOTAUTH => 9, "NOTAUTH")
794
795    /// Name not contained in zone.
796    ///
797    /// A name used in the prerequisite or update section is not within the
798    /// zone given in the zone section.
799    ///
800    /// Defined in [RFC 2136].
801    ///
802    /// [RFC 2136]: https://tools.ietf.org/html/rfc2136
803    (NOTZONE => 10, "NOTZONE")
804
805    /// TSIG signature failure.
806    ///
807    /// The TSIG signature fails to verify.
808    ///
809    /// Defined in [RFC 2845].
810    ///
811    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
812    (BADSIG => 16, "BADSIG")
813
814    /// Key not recognized.
815    ///
816    /// The server did not recognize the key used for generating the
817    /// signature.
818    ///
819    /// Defined in [RFC 2845].
820    ///
821    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
822    (BADKEY => 17, "BADKEY")
823
824    /// Signature out of time window.
825    ///
826    /// The server time was outside the time interval specified by the
827    /// request.
828    ///
829    /// Defined in [RFC 2845].
830    ///
831    /// [RFC 2845]: https://tools.ietf.org/html/rfc2845
832    (BADTIME => 18, "BADTIME")
833
834    /// Bad TKEY mode.
835    ///
836    /// The mode field in a TKEY resource record contained a mode not
837    /// supported by the server.
838    ///
839    /// Defined in [RFC 2930].
840    ///
841    /// [RFC 2930]: https://tools.ietf.org/html/rfc2930
842    (BADMODE => 19, "BADMODE")
843
844    /// Duplicate key name.
845    ///
846    /// In TKEY records, when establishing a new key, the name used already
847    /// exists at the server or when deleting a key, a key of this name does
848    /// not exist.
849    ///
850    /// Defined in [RFC 2930].
851    ///
852    /// [RFC 2930]: https://tools.ietf.org/html/rfc2930
853    (BADNAME => 20, "BADNAME")
854
855    /// Algorithm not supported.
856    ///
857    /// The value is defined in [RFC 2930] but never actually explained.
858    /// Presumably, it will be returned when the algorithm field of a TKEY
859    /// record contains a value not supported by the server.
860    ///
861    /// [RFC 2930]: https://tools.ietf.org/html/rfc2930
862    (BADALG => 21, "BADALG")
863
864    /// Bad truncation.
865    ///
866    /// A TSIG record was received with a MAC too short for the local
867    /// policy in force.
868    ///
869    /// Defined in [RFC 4635].
870    ///
871    /// [RFC 4635]: https://tools.ietf.org/html/rfc4635
872    (BADTRUNC => 22, "BADTRUNC")
873
874    /// Bad or missing server cookie.
875    ///
876    /// The request contained a COOKIE option either without a server cookie
877    /// or with a server cookie that did not validate.
878    ///
879    /// Defined in [RFC 7873].
880    ///
881    /// [RFC 7873]: https://tools.ietf.org/html/rfc7873
882    (BADCOOKIE => 23, "BADCOOKIE")
883}
884
885//--- From
886
887impl From<Rcode> for TsigRcode {
888    fn from(value: Rcode) -> TsigRcode {
889        TsigRcode::from_int(u16::from(value.to_int()))
890    }
891}
892
893impl From<OptRcode> for TsigRcode {
894    fn from(value: OptRcode) -> TsigRcode {
895        TsigRcode::from_int(value.to_int())
896    }
897}
898
899int_enum_str_with_decimal!(TsigRcode, u16, "unknown TSIG error");
900int_enum_zonefile_fmt_with_decimal!(TsigRcode);
901
902//============ Error Types ===================================================
903
904/// An integer couldn’t be converted into an rcode.
905#[derive(Clone, Copy, Debug)]
906pub struct InvalidRcode(());
907
908impl fmt::Display for InvalidRcode {
909    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
910        f.write_str("invalid rcode value")
911    }
912}
913
914#[cfg(feature = "std")]
915impl std::error::Error for InvalidRcode {}
916
917//============ Tests =========================================================
918
919#[cfg(test)]
920mod test {
921    use super::*;
922
923    #[test]
924    fn optrcode_parts() {
925        // Define a macro to test the various functions involved in working
926        // with RFC 6891 extended RCODEs. Given an OPT RCODE enum variant,
927        // check if the functions produce the expected high bit, low bit and
928        // combined values.
929        macro_rules! assert_opt_rcode_parts_eq {
930            ($name:expr, $high_bits:expr, $low_bits:expr) => {
931                let u12 = (($high_bits as u16) << 4) | ($low_bits as u16);
932                assert_eq!($name.rcode().to_int(), $low_bits);
933                assert_eq!($name.ext(), $high_bits);
934                assert_eq!($name.to_parts().0.to_int(), $low_bits);
935                assert_eq!($name.to_parts().1, $high_bits);
936                assert_eq!($name.to_int(), u12);
937            };
938        }
939
940        // Test RFC 6891 OptRcode enum variants that domain defines, plus any
941        // boundary cases not included in that set:
942        assert_opt_rcode_parts_eq!(OptRcode::NOERROR, 0b000_0000, 0b0000);
943        assert_opt_rcode_parts_eq!(OptRcode::FORMERR, 0b000_0000, 0b0001);
944        assert_opt_rcode_parts_eq!(OptRcode::SERVFAIL, 0b000_0000, 0b0010);
945        assert_opt_rcode_parts_eq!(OptRcode::NXDOMAIN, 0b000_0000, 0b0011);
946        assert_opt_rcode_parts_eq!(OptRcode::NOTIMP, 0b000_0000, 0b0100);
947        assert_opt_rcode_parts_eq!(OptRcode::REFUSED, 0b000_0000, 0b0101);
948        assert_opt_rcode_parts_eq!(OptRcode::YXDOMAIN, 0b000_0000, 0b0110);
949        assert_opt_rcode_parts_eq!(OptRcode::YXRRSET, 0b000_0000, 0b0111);
950        assert_opt_rcode_parts_eq!(OptRcode::NXRRSET, 0b000_0000, 0b1000);
951        assert_opt_rcode_parts_eq!(OptRcode::NOTAUTH, 0b000_0000, 0b1001);
952        assert_opt_rcode_parts_eq!(OptRcode::NOTZONE, 0b000_0000, 0b1010);
953        assert_opt_rcode_parts_eq!(OptRcode(15), 0b0000_0000, 0b1111);
954        assert_opt_rcode_parts_eq!(OptRcode::BADVERS, 0b0000_0001, 0b0000);
955        assert_opt_rcode_parts_eq!(OptRcode(17), 0b0000_0001, 0b0001);
956        assert_opt_rcode_parts_eq!(OptRcode::BADCOOKIE, 0b000_0001, 0b0111);
957        assert_opt_rcode_parts_eq!(OptRcode(4094), 0b1111_1111, 0b1110);
958        assert_opt_rcode_parts_eq!(OptRcode(4095), 0b1111_1111, 0b1111);
959    }
960
961    #[test]
962    fn rcode_fromstr() {
963        assert_eq!(Ok(Rcode::NOERROR), "NOERROR".parse());
964        assert_eq!(Ok(Rcode::FORMERR), "FORMERR".parse());
965        assert_eq!(Ok(Rcode::SERVFAIL), "SERVFAIL".parse());
966        assert_eq!(Ok(Rcode::NXDOMAIN), "NXDOMAIN".parse());
967        assert_eq!(Ok(Rcode::NOTIMP), "NOTIMP".parse());
968        assert_eq!(Ok(Rcode::REFUSED), "REFUSED".parse());
969        assert_eq!(Ok(Rcode::YXDOMAIN), "YXDOMAIN".parse());
970        assert_eq!(Ok(Rcode::YXRRSET), "YXRRSET".parse());
971        assert_eq!(Ok(Rcode::NXRRSET), "NXRRSET".parse());
972        assert_eq!(Ok(Rcode::NOTAUTH), "NOTAUTH".parse());
973        assert_eq!(Ok(Rcode::NOTZONE), "NOTZONE".parse());
974        assert!("#$%!@".parse::<Rcode>().is_err());
975    }
976
977    #[test]
978    fn optrcode_fromstr() {
979        assert_eq!(Ok(OptRcode::NOERROR), "NOERROR".parse());
980        assert_eq!(Ok(OptRcode::FORMERR), "FORMERR".parse());
981        assert_eq!(Ok(OptRcode::SERVFAIL), "SERVFAIL".parse());
982        assert_eq!(Ok(OptRcode::NXDOMAIN), "NXDOMAIN".parse());
983        assert_eq!(Ok(OptRcode::NOTIMP), "NOTIMP".parse());
984        assert_eq!(Ok(OptRcode::REFUSED), "REFUSED".parse());
985        assert_eq!(Ok(OptRcode::YXDOMAIN), "YXDOMAIN".parse());
986        assert_eq!(Ok(OptRcode::YXRRSET), "YXRRSET".parse());
987        assert_eq!(Ok(OptRcode::NXRRSET), "NXRRSET".parse());
988        assert_eq!(Ok(OptRcode::NOTAUTH), "NOTAUTH".parse());
989        assert_eq!(Ok(OptRcode::NOTZONE), "NOTZONE".parse());
990        assert_eq!(Ok(OptRcode::BADVERS), "BADVERS".parse());
991        assert_eq!(Ok(OptRcode::BADCOOKIE), "BADCOOKIE".parse());
992        assert!("#$%!@".parse::<Rcode>().is_err());
993    }
994
995    #[test]
996    fn optrcode_isext() {
997        assert!(!OptRcode::NOERROR.is_ext());
998        assert!(!OptRcode::FORMERR.is_ext());
999        assert!(!OptRcode::SERVFAIL.is_ext());
1000        assert!(!OptRcode::NXDOMAIN.is_ext());
1001        assert!(!OptRcode::NOTIMP.is_ext());
1002        assert!(!OptRcode::REFUSED.is_ext());
1003        assert!(!OptRcode::YXDOMAIN.is_ext());
1004        assert!(!OptRcode::YXRRSET.is_ext());
1005        assert!(!OptRcode::NXRRSET.is_ext());
1006        assert!(!OptRcode::NOTAUTH.is_ext());
1007        assert!(!OptRcode::NOTZONE.is_ext());
1008        assert!(OptRcode::BADVERS.is_ext());
1009        assert!(OptRcode::BADCOOKIE.is_ext());
1010    }
1011}