domain_core/bits/name/
builder.rs

1//! Building a domain name.
2//!
3//! This is a private module for tidiness. `DnameBuilder` and `PushError`
4//! are re-exported by the parent module.
5
6use bytes::{BufMut, BytesMut};
7use super::dname::Dname;
8use super::relative::RelativeDname;
9use super::traits::{ToDname, ToRelativeDname};
10
11
12//------------ DnameBuilder --------------------------------------------------
13
14/// Builds a domain name step by step by appending data.
15/// 
16/// The domain name builder is the most fundamental way to construct a new
17/// domain name. It wraps a [`BytesMut`] value and allows adding single bytes,
18/// byte slices, or entire labels.
19///
20/// Unlike a [`BytesMut`], the name builder will take care of growing the
21/// buffer if there isn’t enough space. It will, however, do so only once. If
22/// it runs out of space, it will grow the buffer to 255 bytes, since that is
23/// the maximum length of a domain name.
24///
25/// [`BytesMut`]: ../../../bytes/struct.BytesMut.html
26#[derive(Clone)]
27pub struct DnameBuilder {
28    /// The buffer to build the name in.
29    bytes: BytesMut,
30
31    /// The position in `bytes` where the current label started.
32    ///
33    /// If this is `None` we do not have a label currently.
34    head: Option<usize>,
35}
36
37impl DnameBuilder {
38    /// Creates a new domain name builder from an existing bytes buffer.
39    ///
40    /// Whatever is in the buffer already is considered to be a relative
41    /// domain name. Since that may not be the case, this function is
42    /// unsafe.
43    pub(super) unsafe fn from_bytes(bytes: BytesMut) -> Self {
44        DnameBuilder { bytes, head: None }
45    }
46
47    /// Creates a domain name builder with default capacity.
48    ///
49    /// The capacity will be just large enough that the underlying `BytesMut`
50    /// value does not need to allocate. On a 64 bit system, that will be 31
51    /// bytes, with 15 bytes on a 32 bit system. Either should be enough to
52    /// hold most common domain names.
53    pub fn new() -> Self {
54        unsafe {
55            DnameBuilder::from_bytes(BytesMut::new())
56        }
57    }
58
59    /// Creates a domain name builder with a given capacity.
60    ///
61    /// The `capacity` may be larger than 255, even if the resulting domain
62    /// name will never be.
63    pub fn with_capacity(capacity: usize) -> Self {
64        unsafe {
65            DnameBuilder::from_bytes(BytesMut::with_capacity(capacity))
66        }
67    }
68
69    /// Returns the current length of the domain name.
70    pub fn len(&self) -> usize {
71        self.bytes.len()
72    }
73
74    /// Returns whether the builder is empty.
75    pub fn is_empty(&self) -> bool {
76        self.bytes.is_empty()
77    }
78
79    /// Returns the capacity of the underlying bytes buffer.
80    ///
81    /// A domain name can be up to 255 bytes in size independently of what
82    /// this method returns.
83    pub fn capacity(&self) -> usize {
84        self.bytes.capacity()
85    }
86
87    /// Returns whether there currently is a label under construction.
88    ///
89    /// This returns `false` if the name is still empty or if the last thing
90    /// that happend was a call to [`end_label`].
91    ///
92    /// [`end_label`]: #method.end_label
93    pub fn in_label(&self) -> bool {
94        self.head.is_some()
95    }
96
97    /// Pushes a byte to the end of the domain name.
98    ///
99    /// Starts a new label if necessary. Returns an error if pushing the byte
100    /// would exceed the size limits for labels or domain names.
101    pub fn push(&mut self, ch: u8) -> Result<(), PushError> {
102        let len = self.bytes.len();
103        if len >= 254 {
104            return Err(PushError::LongName);
105        }
106        if let Some(head) = self.head {
107            if len - head > 63 {
108                return Err(PushError::LongLabel)
109            }
110            self.ensure_capacity(1);
111        }
112        else {
113            self.head = Some(len);
114            self.ensure_capacity(2);
115            self.bytes.put_u8(0)
116        }
117        self.bytes.put_u8(ch);
118        Ok(())
119    }
120
121    /// Appends a byte slice to the end of the domain name.
122    ///
123    /// Starts a new label if necessary. Returns an error if pushing
124    /// would exceed the size limits for labels or domain names.
125    ///
126    /// If bytes is empty, does absolutely nothing.
127    pub fn append<T: AsRef<[u8]>>(&mut self, bytes: T)
128                                  -> Result<(), PushError> {
129        let bytes = bytes.as_ref();
130        if bytes.is_empty() {
131            return Ok(())
132        }
133        if let Some(head) = self.head {
134            if bytes.len() > 63 - (self.bytes.len() - head) {
135                return Err(PushError::LongLabel)
136            }
137        }
138        else {
139            if bytes.len() > 63 {
140                return Err(PushError::LongLabel)
141            }
142            if self.bytes.len() + bytes.len() > 254 {
143                return Err(PushError::LongName)
144            }
145            self.head = Some(self.bytes.len());
146            self.ensure_capacity(1);
147            self.bytes.put_u8(0)
148        }
149        self.ensure_capacity(bytes.len());
150        self.bytes.put_slice(bytes);
151        Ok(())
152    }
153
154    /// Ensures that there is enough capacity for `additional` bytes.
155    ///
156    /// The argument `additional` is only used to check whether adding that
157    /// many bytes will exceed the current capacity. If it does, the buffer
158    /// will be grown to a total capacity of 255 bytes. It will *not* be
159    /// grown to `additional` bytes.
160    fn ensure_capacity(&mut self, additional: usize) {
161        if self.bytes.remaining_mut() < additional {
162            let additional = 255 - self.bytes.len();
163            self.bytes.reserve(additional)
164        }
165    }
166
167    /// Ends the current label.
168    ///
169    /// If there isn’t a current label, does nothing.
170    pub fn end_label(&mut self) {
171        if let Some(head) = self.head {
172            let len = self.bytes.len() - head - 1;
173            self.bytes[head] = len as u8;
174            self.head = None;
175        }
176    }
177
178    /// Appends a byte slice as a complete label.
179    ///
180    /// If there currently is a label under construction, it will be ended
181    /// before appending `label`.
182    ///
183    /// Returns an error if `label` exceeds the label size limit of 63 bytes
184    /// or appending the label would exceed the domain name size limit of
185    /// 255 bytes.
186    pub fn append_label<T: AsRef<[u8]>>(&mut self, label: T)
187                                        -> Result<(), PushError> {
188        let head = self.head;
189        self.end_label();
190        if let Err(err) = self.append(label) {
191            self.head = head;
192            return Err(err)
193        }
194        self.end_label();
195        Ok(())
196    }
197
198    /// Appends a relative domain name.
199    ///
200    /// If there currently is a lable under construction, it will be ended
201    /// before appending `name`.
202    ///
203    /// Returns an error if appending would result in a name longer than 254
204    /// bytes.
205    //
206    //  XXX NEEDS TESTS
207    pub fn append_name<N: ToRelativeDname>(&mut self, name: &N)
208                                           -> Result<(), PushNameError> {
209        let head = self.head.take();
210        self.end_label();
211        if self.bytes.len() + name.compose_len() > 254 {
212            self.head = head;
213            return Err(PushNameError)
214        }
215        self.ensure_capacity(name.compose_len());
216        name.compose(&mut self.bytes);
217        Ok(())
218    }
219
220    /// Finishes building the name and returns the resulting domain name.
221    /// 
222    /// If there currently is a label being built, ends the label first
223    /// before returning the name. I.e., you don’t have to call [`end_label`]
224    /// explicitely.
225    ///
226    /// [`end_label`]: #method.end_label
227    pub fn finish(mut self) -> RelativeDname {
228        self.end_label();
229        unsafe { RelativeDname::from_bytes_unchecked(self.bytes.freeze()) }
230    }
231
232    /// Appends the root label to the name and returns it as a `Dname`.
233    ///
234    /// If there currently is a label under construction, ends the label.
235    /// Then adds the empty root label and transforms the name into a
236    /// `Dname`. As adding the root label may push the name over the size
237    /// limit, this may return an error.
238    //
239    // XXX I don’t think adding the root label will actually ever push the
240    //     builder over the limit. Double check and if true, change the return
241    //     type.
242    pub fn into_dname(mut self) -> Result<Dname, PushNameError> {
243        self.end_label();
244        if self.bytes.len() >= 255 {
245            Err(PushNameError)
246        }
247        else {
248            self.ensure_capacity(1);
249            self.bytes.put_u8(0);
250            Ok(unsafe { Dname::from_bytes_unchecked(self.bytes.freeze()) })
251        }
252    }
253
254    /// Appends an origin and returns the resulting `Dname`.
255    /// If there currently is a label under construction, ends the label.
256    /// Then adds the `origin` and transforms the name into a
257    /// `Dname`. 
258    //
259    //  XXX NEEDS TESTS
260    pub fn append_origin<N: ToDname>(mut self, origin: &N)
261                                     -> Result<Dname, PushNameError> {
262        self.end_label();
263        if self.bytes.len() + origin.compose_len() > 255 {
264            return Err(PushNameError)
265        }
266        self.ensure_capacity(origin.compose_len());
267        origin.compose(&mut self.bytes);
268        Ok(unsafe { Dname::from_bytes_unchecked(self.bytes.freeze()) })
269    }
270}
271
272
273//--- Default
274
275impl Default for DnameBuilder {
276    fn default() -> Self {
277        Self::new()
278    }
279}
280
281
282//------------ PushError -----------------------------------------------------
283
284/// An error happened while trying to push data to a domain name builder.
285#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
286pub enum PushError {
287    /// The current label would exceed the limit of 63 bytes.
288    #[fail(display="long label")]
289    LongLabel,
290
291    /// The name would exceed the limit of 255 bytes.
292    #[fail(display="long domain name")]
293    LongName,
294}
295
296
297//------------ PushNameError -------------------------------------------------
298
299/// An error happened while trying to push a name to a domain name builder.
300#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
301#[fail(display="long domain name")]
302pub struct PushNameError;
303
304
305//============ Testing =======================================================
306
307#[cfg(test)]
308mod test {
309    use super::*;
310
311    #[test]
312    fn build() {
313        let mut builder = DnameBuilder::with_capacity(255);
314        builder.push(b'w').unwrap();
315        builder.append(b"ww").unwrap();
316        builder.end_label();
317        builder.append(b"exa").unwrap();
318        builder.push(b'm').unwrap();
319        builder.push(b'p').unwrap();
320        builder.append(b"le").unwrap();
321        builder.end_label();
322        builder.append(b"com").unwrap();
323        assert_eq!(builder.finish().as_slice(),
324                   b"\x03www\x07example\x03com");
325    }
326
327    #[test]
328    fn build_by_label() {
329        let mut builder = DnameBuilder::with_capacity(255);
330        builder.append_label(b"www").unwrap();
331        builder.append_label(b"example").unwrap();
332        builder.append_label(b"com").unwrap();
333        assert_eq!(builder.finish().as_slice(),
334                   b"\x03www\x07example\x03com");
335    }
336
337    #[test]
338    fn build_mixed() {
339        let mut builder = DnameBuilder::with_capacity(255);
340        builder.push(b'w').unwrap();
341        builder.append(b"ww").unwrap();
342        builder.append_label(b"example").unwrap();
343        builder.append(b"com").unwrap();
344        assert_eq!(builder.finish().as_slice(),
345                   b"\x03www\x07example\x03com");
346    }
347
348    #[test]
349    fn buf_growth() {
350        let mut builder = DnameBuilder::new();
351        builder.append_label(b"1234567890").unwrap();
352        builder.append_label(b"1234567890").unwrap();
353        builder.append_label(b"1234567890").unwrap();
354        builder.append_label(b"1234567890").unwrap();
355        assert!(builder.capacity() >= 255 && builder.capacity() < 1024);
356        assert_eq!(
357            builder.finish().as_slice(),
358            &b"\x0a1234567890\x0a1234567890\x0a1234567890\x0a1234567890"[..]
359        );
360    }
361
362    #[test]
363    fn name_limit() {
364        let mut builder = DnameBuilder::new();
365        for _ in 0..25 {
366            // 9 bytes label is 10 bytes in total 
367            builder.append_label(b"123456789").unwrap();
368        }
369
370        assert_eq!(builder.append_label(b"12345"), Err(PushError::LongName));
371        assert_eq!(builder.clone().append_label(b"1234"), Ok(()));
372
373        assert_eq!(builder.append(b"12345"), Err(PushError::LongName));
374        assert_eq!(builder.clone().append(b"1234"), Ok(()));
375
376        assert_eq!(builder.append(b"12"), Ok(()));
377        assert_eq!(builder.push(b'3'), Ok(()));
378        assert_eq!(builder.push(b'4'), Err(PushError::LongName))
379    }
380
381    #[test]
382    fn label_limit() {
383        let mut builder = DnameBuilder::new();
384        builder.append_label(&[0u8; 63][..]).unwrap();
385        assert_eq!(builder.append_label(&[0u8; 64][..]),
386                   Err(PushError::LongLabel));
387        assert_eq!(builder.append_label(&[0u8; 164][..]),
388                   Err(PushError::LongLabel));
389
390        builder.append(&[0u8; 60][..]).unwrap();
391        let _ = builder.clone().append_label(b"123").unwrap();
392        assert_eq!(builder.append(b"1234"), Err(PushError::LongLabel));
393        builder.append(b"12").unwrap();
394        builder.push(b'3').unwrap();
395        assert_eq!(builder.push(b'4'), Err(PushError::LongLabel));
396    }
397
398    #[test]
399    fn finish() {
400        let mut builder = DnameBuilder::new();
401        builder.append_label(b"www").unwrap();
402        builder.append_label(b"example").unwrap();
403        builder.append(b"com").unwrap();
404        assert_eq!(builder.finish().as_slice(),
405                   b"\x03www\x07example\x03com");
406    }
407
408    #[test]
409    fn into_dname() {
410        let mut builder = DnameBuilder::new();
411        builder.append_label(b"www").unwrap();
412        builder.append_label(b"example").unwrap();
413        builder.append(b"com").unwrap();
414        assert_eq!(builder.into_dname().unwrap().as_slice(),
415                   b"\x03www\x07example\x03com\x00");
416    }
417
418    #[test]
419    fn into_dname_limit() {
420        let mut builder = DnameBuilder::new();
421        for _ in 0..51 {
422            builder.append_label(b"1234").unwrap();
423        }
424        assert_eq!(builder.len(), 255);
425        assert_eq!(builder.into_dname(), Err(PushNameError));
426    }
427}
428