domain-core 0.4.0

A DNS library for Rust – Core.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
//! Building a domain name.
//!
//! This is a private module for tidiness. `DnameBuilder` and `PushError`
//! are re-exported by the parent module.

use bytes::{BufMut, BytesMut};
use super::dname::Dname;
use super::relative::RelativeDname;
use super::traits::{ToDname, ToRelativeDname};


//------------ DnameBuilder --------------------------------------------------

/// Builds a domain name step by step by appending data.
/// 
/// The domain name builder is the most fundamental way to construct a new
/// domain name. It wraps a [`BytesMut`] value and allows adding single bytes,
/// byte slices, or entire labels.
///
/// Unlike a [`BytesMut`], the name builder will take care of growing the
/// buffer if there isn’t enough space. It will, however, do so only once. If
/// it runs out of space, it will grow the buffer to 255 bytes, since that is
/// the maximum length of a domain name.
///
/// [`BytesMut`]: ../../../bytes/struct.BytesMut.html
#[derive(Clone)]
pub struct DnameBuilder {
    /// The buffer to build the name in.
    bytes: BytesMut,

    /// The position in `bytes` where the current label started.
    ///
    /// If this is `None` we do not have a label currently.
    head: Option<usize>,
}

impl DnameBuilder {
    /// Creates a new domain name builder from an existing bytes buffer.
    ///
    /// Whatever is in the buffer already is considered to be a relative
    /// domain name. Since that may not be the case, this function is
    /// unsafe.
    pub(super) unsafe fn from_bytes(bytes: BytesMut) -> Self {
        DnameBuilder { bytes, head: None }
    }

    /// Creates a domain name builder with default capacity.
    ///
    /// The capacity will be just large enough that the underlying `BytesMut`
    /// value does not need to allocate. On a 64 bit system, that will be 31
    /// bytes, with 15 bytes on a 32 bit system. Either should be enough to
    /// hold most common domain names.
    pub fn new() -> Self {
        unsafe {
            DnameBuilder::from_bytes(BytesMut::new())
        }
    }

    /// Creates a domain name builder with a given capacity.
    ///
    /// The `capacity` may be larger than 255, even if the resulting domain
    /// name will never be.
    pub fn with_capacity(capacity: usize) -> Self {
        unsafe {
            DnameBuilder::from_bytes(BytesMut::with_capacity(capacity))
        }
    }

    /// Returns the current length of the domain name.
    pub fn len(&self) -> usize {
        self.bytes.len()
    }

    /// Returns whether the builder is empty.
    pub fn is_empty(&self) -> bool {
        self.bytes.is_empty()
    }

    /// Returns the capacity of the underlying bytes buffer.
    ///
    /// A domain name can be up to 255 bytes in size independently of what
    /// this method returns.
    pub fn capacity(&self) -> usize {
        self.bytes.capacity()
    }

    /// Returns whether there currently is a label under construction.
    ///
    /// This returns `false` if the name is still empty or if the last thing
    /// that happend was a call to [`end_label`].
    ///
    /// [`end_label`]: #method.end_label
    pub fn in_label(&self) -> bool {
        self.head.is_some()
    }

    /// Pushes a byte to the end of the domain name.
    ///
    /// Starts a new label if necessary. Returns an error if pushing the byte
    /// would exceed the size limits for labels or domain names.
    pub fn push(&mut self, ch: u8) -> Result<(), PushError> {
        let len = self.bytes.len();
        if len >= 254 {
            return Err(PushError::LongName);
        }
        if let Some(head) = self.head {
            if len - head > 63 {
                return Err(PushError::LongLabel)
            }
            self.ensure_capacity(1);
        }
        else {
            self.head = Some(len);
            self.ensure_capacity(2);
            self.bytes.put_u8(0)
        }
        self.bytes.put_u8(ch);
        Ok(())
    }

    /// Appends a byte slice to the end of the domain name.
    ///
    /// Starts a new label if necessary. Returns an error if pushing
    /// would exceed the size limits for labels or domain names.
    ///
    /// If bytes is empty, does absolutely nothing.
    pub fn append<T: AsRef<[u8]>>(&mut self, bytes: T)
                                  -> Result<(), PushError> {
        let bytes = bytes.as_ref();
        if bytes.is_empty() {
            return Ok(())
        }
        if let Some(head) = self.head {
            if bytes.len() > 63 - (self.bytes.len() - head) {
                return Err(PushError::LongLabel)
            }
        }
        else {
            if bytes.len() > 63 {
                return Err(PushError::LongLabel)
            }
            if self.bytes.len() + bytes.len() > 254 {
                return Err(PushError::LongName)
            }
            self.head = Some(self.bytes.len());
            self.ensure_capacity(1);
            self.bytes.put_u8(0)
        }
        self.ensure_capacity(bytes.len());
        self.bytes.put_slice(bytes);
        Ok(())
    }

    /// Ensures that there is enough capacity for `additional` bytes.
    ///
    /// The argument `additional` is only used to check whether adding that
    /// many bytes will exceed the current capacity. If it does, the buffer
    /// will be grown to a total capacity of 255 bytes. It will *not* be
    /// grown to `additional` bytes.
    fn ensure_capacity(&mut self, additional: usize) {
        if self.bytes.remaining_mut() < additional {
            let additional = 255 - self.bytes.len();
            self.bytes.reserve(additional)
        }
    }

    /// Ends the current label.
    ///
    /// If there isn’t a current label, does nothing.
    pub fn end_label(&mut self) {
        if let Some(head) = self.head {
            let len = self.bytes.len() - head - 1;
            self.bytes[head] = len as u8;
            self.head = None;
        }
    }

    /// Appends a byte slice as a complete label.
    ///
    /// If there currently is a label under construction, it will be ended
    /// before appending `label`.
    ///
    /// Returns an error if `label` exceeds the label size limit of 63 bytes
    /// or appending the label would exceed the domain name size limit of
    /// 255 bytes.
    pub fn append_label<T: AsRef<[u8]>>(&mut self, label: T)
                                        -> Result<(), PushError> {
        let head = self.head;
        self.end_label();
        if let Err(err) = self.append(label) {
            self.head = head;
            return Err(err)
        }
        self.end_label();
        Ok(())
    }

    /// Appends a relative domain name.
    ///
    /// If there currently is a lable under construction, it will be ended
    /// before appending `name`.
    ///
    /// Returns an error if appending would result in a name longer than 254
    /// bytes.
    //
    //  XXX NEEDS TESTS
    pub fn append_name<N: ToRelativeDname>(&mut self, name: &N)
                                           -> Result<(), PushNameError> {
        let head = self.head.take();
        self.end_label();
        if self.bytes.len() + name.compose_len() > 254 {
            self.head = head;
            return Err(PushNameError)
        }
        self.ensure_capacity(name.compose_len());
        name.compose(&mut self.bytes);
        Ok(())
    }

    /// Finishes building the name and returns the resulting domain name.
    /// 
    /// If there currently is a label being built, ends the label first
    /// before returning the name. I.e., you don’t have to call [`end_label`]
    /// explicitely.
    ///
    /// [`end_label`]: #method.end_label
    pub fn finish(mut self) -> RelativeDname {
        self.end_label();
        unsafe { RelativeDname::from_bytes_unchecked(self.bytes.freeze()) }
    }

    /// Appends the root label to the name and returns it as a `Dname`.
    ///
    /// If there currently is a label under construction, ends the label.
    /// Then adds the empty root label and transforms the name into a
    /// `Dname`. As adding the root label may push the name over the size
    /// limit, this may return an error.
    //
    // XXX I don’t think adding the root label will actually ever push the
    //     builder over the limit. Double check and if true, change the return
    //     type.
    pub fn into_dname(mut self) -> Result<Dname, PushNameError> {
        self.end_label();
        if self.bytes.len() >= 255 {
            Err(PushNameError)
        }
        else {
            self.ensure_capacity(1);
            self.bytes.put_u8(0);
            Ok(unsafe { Dname::from_bytes_unchecked(self.bytes.freeze()) })
        }
    }

    /// Appends an origin and returns the resulting `Dname`.
    /// If there currently is a label under construction, ends the label.
    /// Then adds the `origin` and transforms the name into a
    /// `Dname`. 
    //
    //  XXX NEEDS TESTS
    pub fn append_origin<N: ToDname>(mut self, origin: &N)
                                     -> Result<Dname, PushNameError> {
        self.end_label();
        if self.bytes.len() + origin.compose_len() > 255 {
            return Err(PushNameError)
        }
        self.ensure_capacity(origin.compose_len());
        origin.compose(&mut self.bytes);
        Ok(unsafe { Dname::from_bytes_unchecked(self.bytes.freeze()) })
    }
}


//--- Default

impl Default for DnameBuilder {
    fn default() -> Self {
        Self::new()
    }
}


//------------ PushError -----------------------------------------------------

/// An error happened while trying to push data to a domain name builder.
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum PushError {
    /// The current label would exceed the limit of 63 bytes.
    #[fail(display="long label")]
    LongLabel,

    /// The name would exceed the limit of 255 bytes.
    #[fail(display="long domain name")]
    LongName,
}


//------------ PushNameError -------------------------------------------------

/// An error happened while trying to push a name to a domain name builder.
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
#[fail(display="long domain name")]
pub struct PushNameError;


//============ Testing =======================================================

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn build() {
        let mut builder = DnameBuilder::with_capacity(255);
        builder.push(b'w').unwrap();
        builder.append(b"ww").unwrap();
        builder.end_label();
        builder.append(b"exa").unwrap();
        builder.push(b'm').unwrap();
        builder.push(b'p').unwrap();
        builder.append(b"le").unwrap();
        builder.end_label();
        builder.append(b"com").unwrap();
        assert_eq!(builder.finish().as_slice(),
                   b"\x03www\x07example\x03com");
    }

    #[test]
    fn build_by_label() {
        let mut builder = DnameBuilder::with_capacity(255);
        builder.append_label(b"www").unwrap();
        builder.append_label(b"example").unwrap();
        builder.append_label(b"com").unwrap();
        assert_eq!(builder.finish().as_slice(),
                   b"\x03www\x07example\x03com");
    }

    #[test]
    fn build_mixed() {
        let mut builder = DnameBuilder::with_capacity(255);
        builder.push(b'w').unwrap();
        builder.append(b"ww").unwrap();
        builder.append_label(b"example").unwrap();
        builder.append(b"com").unwrap();
        assert_eq!(builder.finish().as_slice(),
                   b"\x03www\x07example\x03com");
    }

    #[test]
    fn buf_growth() {
        let mut builder = DnameBuilder::new();
        builder.append_label(b"1234567890").unwrap();
        builder.append_label(b"1234567890").unwrap();
        builder.append_label(b"1234567890").unwrap();
        builder.append_label(b"1234567890").unwrap();
        assert!(builder.capacity() >= 255 && builder.capacity() < 1024);
        assert_eq!(
            builder.finish().as_slice(),
            &b"\x0a1234567890\x0a1234567890\x0a1234567890\x0a1234567890"[..]
        );
    }

    #[test]
    fn name_limit() {
        let mut builder = DnameBuilder::new();
        for _ in 0..25 {
            // 9 bytes label is 10 bytes in total 
            builder.append_label(b"123456789").unwrap();
        }

        assert_eq!(builder.append_label(b"12345"), Err(PushError::LongName));
        assert_eq!(builder.clone().append_label(b"1234"), Ok(()));

        assert_eq!(builder.append(b"12345"), Err(PushError::LongName));
        assert_eq!(builder.clone().append(b"1234"), Ok(()));

        assert_eq!(builder.append(b"12"), Ok(()));
        assert_eq!(builder.push(b'3'), Ok(()));
        assert_eq!(builder.push(b'4'), Err(PushError::LongName))
    }

    #[test]
    fn label_limit() {
        let mut builder = DnameBuilder::new();
        builder.append_label(&[0u8; 63][..]).unwrap();
        assert_eq!(builder.append_label(&[0u8; 64][..]),
                   Err(PushError::LongLabel));
        assert_eq!(builder.append_label(&[0u8; 164][..]),
                   Err(PushError::LongLabel));

        builder.append(&[0u8; 60][..]).unwrap();
        let _ = builder.clone().append_label(b"123").unwrap();
        assert_eq!(builder.append(b"1234"), Err(PushError::LongLabel));
        builder.append(b"12").unwrap();
        builder.push(b'3').unwrap();
        assert_eq!(builder.push(b'4'), Err(PushError::LongLabel));
    }

    #[test]
    fn finish() {
        let mut builder = DnameBuilder::new();
        builder.append_label(b"www").unwrap();
        builder.append_label(b"example").unwrap();
        builder.append(b"com").unwrap();
        assert_eq!(builder.finish().as_slice(),
                   b"\x03www\x07example\x03com");
    }

    #[test]
    fn into_dname() {
        let mut builder = DnameBuilder::new();
        builder.append_label(b"www").unwrap();
        builder.append_label(b"example").unwrap();
        builder.append(b"com").unwrap();
        assert_eq!(builder.into_dname().unwrap().as_slice(),
                   b"\x03www\x07example\x03com\x00");
    }

    #[test]
    fn into_dname_limit() {
        let mut builder = DnameBuilder::new();
        for _ in 0..51 {
            builder.append_label(b"1234").unwrap();
        }
        assert_eq!(builder.len(), 255);
        assert_eq!(builder.into_dname(), Err(PushNameError));
    }
}