mdns_proto/wire/resource_type.rs
1//! DNS resource record types relevant to mDNS (RFC 1035 §3.2.2 + RFC 6762).
2
3use derive_more::{Display, IsVariant, TryUnwrap, Unwrap};
4
5/// Resource record type code.
6#[derive(
7 Debug, Display, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, IsVariant, Unwrap, TryUnwrap,
8)]
9#[display("{}", self.as_str())]
10#[non_exhaustive]
11// The `AAAA` variant keeps the canonical DNS record-type spelling.
12#[allow(clippy::upper_case_acronyms)]
13pub enum ResourceType {
14 /// IPv4 address (`1`).
15 A,
16 /// IPv6 address (`28`).
17 AAAA,
18 /// Domain name pointer (`12`).
19 Ptr,
20 /// Server location (`33`).
21 Srv,
22 /// Text record (`16`).
23 Txt,
24 /// Next secure (`47`, used for negative responses in RFC 6762 §6.1).
25 Nsec,
26 /// Host info (`13`, rarely used).
27 Hinfo,
28 /// Canonical name alias (`5`).
29 Cname,
30 /// Wildcard query type (`255`).
31 Any,
32 /// Lossless escape for unknown rtypes.
33 Unknown(u16),
34}
35
36impl ResourceType {
37 /// Canonical lowercase slug for this resource type.
38 pub const fn as_str(&self) -> &'static str {
39 match self {
40 Self::A => "a",
41 Self::AAAA => "aaaa",
42 Self::Ptr => "ptr",
43 Self::Srv => "srv",
44 Self::Txt => "txt",
45 Self::Nsec => "nsec",
46 Self::Hinfo => "hinfo",
47 Self::Cname => "cname",
48 Self::Any => "any",
49 Self::Unknown(_) => "unknown",
50 }
51 }
52
53 /// Returns the wire-format `u16` value.
54 #[inline(always)]
55 pub const fn to_u16(self) -> u16 {
56 match self {
57 Self::A => 1,
58 Self::AAAA => 28,
59 Self::Ptr => 12,
60 Self::Srv => 33,
61 Self::Txt => 16,
62 Self::Nsec => 47,
63 Self::Hinfo => 13,
64 Self::Cname => 5,
65 Self::Any => 255,
66 Self::Unknown(v) => v,
67 }
68 }
69
70 cfg_heap! {
71 /// Whether this is a standardized RR type whose RDATA contains a (potentially
72 /// compressed) domain name but which this stack does NOT type-specifically
73 /// parse. Per RFC 1035 §3.3 the compression-eligible
74 /// name-bearing types are NS(2), MD(3), MF(4), CNAME(5), SOA(6), MB(7),
75 /// MG(8), MR(9), PTR(12), MINFO(14), MX(15) — plus DNAME(39). We parse CNAME
76 /// and PTR; the rest map to `Unknown` and a sender that knows them MAY
77 /// compress / case-vary their embedded names, so storing raw bytes would be
78 /// compression/case-sensitive and break cache dedup / TTL=0 removal. They are
79 /// not mDNS/DNS-SD record types, so callers DROP them rather than cache a
80 /// non-canonical entry. (RFC 3597 §4: types defined after RFC 1035 are sent
81 /// uncompressed, so genuinely-unknown opaque RDATA is safe to store verbatim.)
82 ///
83 /// Gated to the heap tiers: the sole caller is `Rdata::canonical_rdata_inner`
84 /// (`wire::record`, gated on `alloc` / `std` / `no-atomic`), so without a
85 /// matching gate this is dead code in the `heapless` / core-only tiers and
86 /// trips `#[deny(dead_code)]`.
87 #[inline(always)]
88 pub(crate) const fn is_unhandled_compressible_name(self) -> bool {
89 matches!(self.to_u16(), 2 | 3 | 4 | 6 | 7 | 8 | 9 | 14 | 15 | 39)
90 }
91 }
92
93 /// Reconstructs a `ResourceType` from a wire-format `u16`. Always succeeds —
94 /// unknown values land in `Unknown(v)`.
95 #[inline(always)]
96 pub const fn from_u16(v: u16) -> Self {
97 match v {
98 1 => Self::A,
99 28 => Self::AAAA,
100 12 => Self::Ptr,
101 33 => Self::Srv,
102 16 => Self::Txt,
103 47 => Self::Nsec,
104 13 => Self::Hinfo,
105 5 => Self::Cname,
106 255 => Self::Any,
107 other => Self::Unknown(other),
108 }
109 }
110}
111
112#[cfg(test)]
113#[allow(clippy::unwrap_used)]
114mod tests;