sparkle/
format.rs

1use {SerialNumber, Ttl, std};
2
3/// Associates the set of DNS types for a common format.
4pub trait Format<'a> {
5    type Name: Name<'a>;
6    type RawOctets: AsRef<[u8]> + Clone + std::fmt::Debug + Eq + PartialEq;
7}
8
9/// Encapsulates a domain name.
10pub trait Name<'a>: std::fmt::Display {
11    type LabelIter: Iterator<Item = &'a str>;
12    fn labels(&'a self) -> Self::LabelIter;
13}
14
15/// Encapsulates a question stored in a given format.
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Question<'a, F: Format<'a>> {
18    qname: F::Name,
19    qtype: QType,
20    qclass: QClass,
21}
22
23impl<'a, F: Format<'a>> Question<'a, F> {
24    pub fn new<N: Into<F::Name>>(qname: N, qtype: QType, qclass: QClass) -> Self {
25        Question {
26            qname: qname.into(),
27            qtype: qtype,
28            qclass: qclass,
29        }
30    }
31
32    pub fn qname(&self) -> &F::Name {
33        &self.qname
34    }
35
36    pub fn qtype(&self) -> QType {
37        self.qtype
38    }
39
40    pub fn qclass(&self) -> QClass {
41        self.qclass
42    }
43}
44
45/// Encapsulates a resource record stored in a given format.
46#[derive(Clone, Debug, Eq, PartialEq)]
47pub struct ResourceRecord<'a, F: Format<'a>> {
48    name: F::Name,
49    type_: Type,
50    class: Class,
51    ttl: Ttl,
52    rdata: RData<'a, F>,
53}
54
55impl<'a, F: Format<'a>> ResourceRecord<'a, F> {
56    pub fn new<N: Into<F::Name>, IntoT: Into<Ttl>, R: Into<RData<'a, F>>>(name: N,
57                                                                          type_: Type,
58                                                                          class: Class,
59                                                                          ttl: IntoT,
60                                                                          rdata: R)
61                                                                          -> Self {
62        ResourceRecord {
63            name: name.into(),
64            type_: type_,
65            class: class,
66            ttl: ttl.into(),
67            rdata: rdata.into(),
68        }
69    }
70
71    pub fn name(&self) -> &F::Name {
72        &self.name
73    }
74
75    pub fn type_(&self) -> Type {
76        self.type_
77    }
78
79    pub fn class(&self) -> Class {
80        self.class
81    }
82
83    pub fn ttl(&self) -> Ttl {
84        self.ttl
85    }
86
87    pub fn rdata(&self) -> &RData<'a, F> {
88        &self.rdata
89    }
90}
91
92/// Encapsulates an RDATA field stored in a given format.
93#[derive(Clone, Debug, Eq, PartialEq)]
94pub enum RData<'a, F: Format<'a>> {
95    A { address: std::net::Ipv4Addr },
96    CName { cname: F::Name },
97    NS { nsdname: F::Name },
98    SOA {
99        mname: F::Name,
100        rname: F::Name,
101        serial: SerialNumber,
102        refresh: Ttl,
103        retry: Ttl,
104        expire: Ttl,
105        minimum: Ttl,
106    },
107    Other { octets: F::RawOctets },
108}
109
110/// Encapsulates a CLASS value.
111#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
112pub struct Class(pub u16);
113
114impl Class {
115    /// Returns the underlying CLASS value.
116    pub fn as_u16(&self) -> u16 {
117        self.0
118    }
119}
120
121/// Encapsulates a QCLASS value.
122#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
123pub struct QClass(pub u16);
124
125impl QClass {
126    /// Returns the underlying QCLASS value.
127    pub fn as_u16(&self) -> u16 {
128        self.0
129    }
130}
131
132impl From<QClass> for Class {
133    fn from(x: QClass) -> Self {
134        Class(x.0)
135    }
136}
137
138/// Defines well known CLASS values.
139pub mod class {
140    use super::Class;
141
142    /// Specifies the **Internet** class.
143    pub const IN: Class = Class(1);
144}
145
146/// Defines well known QCLASS values.
147pub mod qclass {
148    use super::QClass;
149
150    /// Specifies the **Internet** class.
151    pub const IN: QClass = QClass(1);
152
153    /// Specifies the **wildcard** (`*`) class.
154    pub const ANY: QClass = QClass(255);
155}
156
157/// Encapsulates a TYPE value.
158#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
159pub struct Type(pub u16);
160
161impl Type {
162    /// Returns the underlying TYPE value.
163    pub fn as_u16(&self) -> u16 {
164        self.0
165    }
166}
167
168/// Encapsulates a QTYPE value.
169#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
170pub struct QType(pub u16);
171
172impl QType {
173    /// Returns the underlying QTYPE value.
174    pub fn as_u16(&self) -> u16 {
175        self.0
176    }
177}
178
179impl From<QType> for Type {
180    fn from(x: QType) -> Self {
181        Type(x.0)
182    }
183}
184
185/// Defines well known TYPE values.
186pub mod type_ {
187    use super::Type;
188
189    /// Specifies the **Internet IPv4 address** record type.
190    pub const A: Type = Type(1);
191
192    /// Specifies the **name server** record type.
193    pub const NS: Type = Type(2);
194
195    /// Specifies the **canonical name** record type.
196    pub const CNAME: Type = Type(5);
197
198    /// Specifies the **start of authority** record type.
199    pub const SOA: Type = Type(6);
200}
201
202/// Defines well known QTYPE values.
203pub mod qtype {
204    use super::QType;
205
206    /// Specifies the **Internet IPv4 address** record type.
207    pub const A: QType = QType(1);
208
209    /// Specifies the **name server** record type.
210    pub const NS: QType = QType(2);
211
212    /// Specifies the **canonical name** record type.
213    pub const CNAME: QType = QType(5);
214
215    /// Specifies the **start of authority** record type.
216    pub const SOA: QType = QType(6);
217
218    /// Specifies the **wildcard** (`*`) record type.
219    pub const ANY: QType = QType(255);
220}
221
222pub fn is_label_valid(s: &[u8]) -> bool {
223    debug_assert!(!s.is_empty());
224    let c = *s.first().unwrap() as char;
225    if !c.is_alphabetic() {
226        return false;
227    }
228    let c = *s.last().unwrap() as char;
229    if !c.is_alphanumeric() {
230        return false;
231    }
232    s.iter().map(|&b| b as char).all(|c| c.is_alphanumeric() || c == '-')
233}
234
235#[cfg(test)]
236mod tests {
237    #[test]
238    fn is_label_valid() {
239        let f = |s: &str| super::is_label_valid(s.as_bytes());
240        assert!(f("a"));
241        assert!(f("alpha"));
242        assert!(f("alpha17"));
243        assert!(f("alpha-bravo"));
244        assert!(f("alpha-bravo17"));
245        assert!(!f("-alpha-bravo17"));
246        assert!(!f("1alpha"));
247        assert!(!f("-alpha"));
248        assert!(!f("alpha-"));
249        assert!(!f("alpha.bravo"));
250    }
251}