use bytes::{BufMut, BytesMut};
use super::dname::Dname;
use super::relative::RelativeDname;
use super::traits::{ToDname, ToRelativeDname};
#[derive(Clone)]
pub struct DnameBuilder {
bytes: BytesMut,
head: Option<usize>,
}
impl DnameBuilder {
pub(super) unsafe fn from_bytes(bytes: BytesMut) -> Self {
DnameBuilder { bytes, head: None }
}
pub fn new() -> Self {
unsafe {
DnameBuilder::from_bytes(BytesMut::new())
}
}
pub fn with_capacity(capacity: usize) -> Self {
unsafe {
DnameBuilder::from_bytes(BytesMut::with_capacity(capacity))
}
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub fn capacity(&self) -> usize {
self.bytes.capacity()
}
pub fn in_label(&self) -> bool {
self.head.is_some()
}
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(())
}
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(())
}
fn ensure_capacity(&mut self, additional: usize) {
if self.bytes.remaining_mut() < additional {
let additional = 255 - self.bytes.len();
self.bytes.reserve(additional)
}
}
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;
}
}
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(())
}
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(())
}
pub fn finish(mut self) -> RelativeDname {
self.end_label();
unsafe { RelativeDname::from_bytes_unchecked(self.bytes.freeze()) }
}
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()) })
}
}
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()) })
}
}
impl Default for DnameBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum PushError {
#[fail(display="long label")]
LongLabel,
#[fail(display="long domain name")]
LongName,
}
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
#[fail(display="long domain name")]
pub struct PushNameError;
#[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 {
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));
}
}