#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::borrow::Cow::{self, Borrowed, Owned};
use std::cell::Cell;
use std::default::Default;
use std::fmt;
use std::io::{Cursor, Read, Write};
use std::mem::{transmute, zeroed};
use std::slice::Iter;
use std::str::from_utf8_unchecked;
use std::vec::IntoIter;
use rand::random;
use idna;
use record::{Class, Record, RecordType};
pub const MESSAGE_LIMIT: usize = 0xffff;
pub const LABEL_LIMIT: usize = 63;
pub const NAME_LIMIT: usize = 255;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct DnsError(pub RCode);
impl fmt::Display for DnsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.0.get_error())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DecodeError {
ExtraneousData,
ShortMessage,
InvalidMessage,
InvalidName,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
DecodeError::ExtraneousData => "extraneous data",
DecodeError::ShortMessage => "short message",
DecodeError::InvalidMessage => "invalid message",
DecodeError::InvalidName => "invalid name",
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum EncodeError {
InvalidName,
TooLong,
}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
EncodeError::InvalidName => f.write_str("invalid name value"),
EncodeError::TooLong => f.write_str("message too long"),
}
}
}
pub struct MsgReader<'a> {
data: Cursor<&'a [u8]>,
}
impl<'a> MsgReader<'a> {
pub fn new(data: &[u8]) -> MsgReader {
MsgReader{data: Cursor::new(data)}
}
pub fn with_offset(data: &[u8], offset: usize) -> MsgReader {
let mut cur = Cursor::new(data);
cur.set_position(offset as u64);
MsgReader{data: cur}
}
pub fn remaining(&self) -> usize {
self.data.get_ref().len() - self.data.position() as usize
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<(), DecodeError> {
match self.data.read(buf) {
Ok(n) if n == buf.len() => Ok(()),
_ => Err(DecodeError::ShortMessage),
}
}
pub fn read_byte(&mut self) -> Result<u8, DecodeError> {
let mut buf = [0];
try!(self.read(&mut buf));
Ok(buf[0])
}
pub fn read_to_end(&mut self) -> Result<Vec<u8>, DecodeError> {
let mut res = Vec::with_capacity(self.remaining());
res.resize(self.remaining(), 0);
try!(self.read(&mut res));
Ok(res)
}
pub fn read_character_string(&mut self) -> Result<Vec<u8>, DecodeError> {
let length_octet = try!(self.read_byte()) as usize;
let mut res = Vec::with_capacity(length_octet);
res.resize(length_octet, 0);
try!(self.read(&mut res));
Ok(res)
}
pub fn read_u16(&mut self) -> Result<u16, DecodeError> {
let mut buf = [0; 2];
try!(self.read(&mut buf));
Ok(u16::from_be(unsafe { transmute(buf) }))
}
pub fn read_u32(&mut self) -> Result<u32, DecodeError> {
let mut buf = [0; 4];
try!(self.read(&mut buf));
Ok(u32::from_be(unsafe { transmute(buf) }))
}
pub fn read_into(&mut self, buf: &mut Vec<u8>, n: usize) -> Result<(), DecodeError> {
let len = buf.len();
buf.resize(len + n, 0);
self.read(&mut buf[len..])
}
pub fn read_name(&mut self) -> Result<String, DecodeError> {
let start_pos = self.data.position();
let mut restore = None;
let mut res = String::new();
let mut total_read = 0;
loop {
let len = try!(self.read_byte());
if len == 0 {
if total_read + 1 > NAME_LIMIT {
return Err(DecodeError::InvalidName);
}
break;
}
let compressed = match len >> 6 {
0b11 => true,
0b00 => false,
_ => return Err(DecodeError::InvalidMessage),
};
if compressed {
let hi = (len & 0b00111111) as u64;
let lo = try!(self.read_byte()) as u64;
let offset = (hi << 8) | lo;
if offset >= start_pos {
return Err(DecodeError::InvalidName);
}
if restore.is_none() {
restore = Some(self.data.position());
}
self.data.set_position(offset);
continue;
}
if total_read + 1 + len as usize > NAME_LIMIT {
return Err(DecodeError::InvalidName);
}
total_read += 1 + len as usize;
try!(self.read_segment(&mut res, len as usize));
}
if res.is_empty() {
res.push('.');
} else {
res.shrink_to_fit();
}
if let Some(pos) = restore {
self.data.set_position(pos);
}
Ok(res)
}
fn read_segment(&mut self, buf: &mut String, len: usize) -> Result<(), DecodeError> {
let mut bytes = [0; 64];
try!(self.read(&mut bytes[..len]));
let seg = &bytes[..len];
if !seg.is_ascii() {
return Err(DecodeError::InvalidName);
}
let s = unsafe { from_utf8_unchecked(seg) };
if !is_valid_segment(s) {
return Err(DecodeError::InvalidName);
}
let label = match idna::to_unicode(s) {
Ok(s) => s,
Err(_) => return Err(DecodeError::InvalidName)
};
buf.push_str(&label);
buf.push('.');
Ok(())
}
fn consume(&mut self, n: u64) {
let p = self.data.position();
self.data.set_position(p + n);
}
fn finish(self) -> Result<(), DecodeError> {
if self.remaining() == 0 {
Ok(())
} else {
Err(DecodeError::ExtraneousData)
}
}
fn read_header(&mut self) -> Result<FullHeader, DecodeError> {
let mut buf = [0; 12];
try!(self.read(&mut buf));
let hdr: HeaderData = unsafe { transmute(buf) };
let id = u16::from_be(hdr.id);
let qr = hdr.flags0 & 0b10000000;
let op = hdr.flags0 & 0b01111000;
let aa = hdr.flags0 & 0b00000100;
let tc = hdr.flags0 & 0b00000010;
let rd = hdr.flags0 & 0b00000001;
let ra = hdr.flags1 & 0b10000000;
let rc = hdr.flags1 & 0b00001111;
let qd_count = u16::from_be(hdr.qd_count);
let an_count = u16::from_be(hdr.an_count);
let ns_count = u16::from_be(hdr.ns_count);
let ar_count = u16::from_be(hdr.ar_count);
Ok(FullHeader{
id: id,
qr: if qr == 0 { Qr::Query } else { Qr::Response },
op: OpCode::from_u8(op),
authoritative: aa != 0,
truncated: tc != 0,
recursion_desired: rd != 0,
recursion_available: ra != 0,
rcode: RCode::from_u8(rc),
qd_count: qd_count,
an_count: an_count,
ns_count: ns_count,
ar_count: ar_count,
})
}
fn read_question(&mut self) -> Result<Question, DecodeError> {
let name = try!(self.read_name());
let mut buf = [0; 4];
try!(self.read(&mut buf));
let msg: QuestionData = unsafe { transmute(buf) };
let q_type = u16::from_be(msg.q_type);
let q_class = u16::from_be(msg.q_class);
Ok(Question{
name: name,
q_type: RecordType::from_u16(q_type),
q_class: Class::from_u16(q_class),
})
}
fn read_resource(&mut self) -> Result<Resource<'a>, DecodeError> {
let name = try!(self.read_name());
let mut buf = [0; 10];
try!(self.read(&mut buf));
let msg: ResourceData = unsafe { transmute(buf) };
let r_type = u16::from_be(msg.r_type);
let r_class = u16::from_be(msg.r_class);
let ttl = u32::from_be(msg.ttl);
let length = u16::from_be(msg.length);
let data = *self.data.get_ref();
let offset = self.data.position() as usize;
let r_data = &data[..offset + length as usize];
self.consume(length as u64);
Ok(Resource{
name: name,
r_type: RecordType::from_u16(r_type),
r_class: Class::from_u16(r_class),
ttl: ttl,
data: Borrowed(r_data),
offset: offset,
})
}
}
pub struct MsgWriter<'a> {
data: Cursor<&'a mut [u8]>,
}
impl<'a> MsgWriter<'a> {
pub fn new(data: &mut [u8]) -> MsgWriter {
MsgWriter{data: Cursor::new(data)}
}
pub fn written(&self) -> usize {
self.data.position() as usize
}
pub fn into_bytes(self) -> &'a [u8] {
let n = self.written();
&self.data.into_inner()[..n]
}
pub fn write(&mut self, data: &[u8]) -> Result<(), EncodeError> {
if self.written() + data.len() > MESSAGE_LIMIT {
Err(EncodeError::TooLong)
} else {
self.data.write_all(data).map_err(|_| EncodeError::TooLong)
}
}
pub fn write_character_string(&mut self, data: &[u8]) -> Result<(), EncodeError> {
let len = data.len();
if len > 255 {
Err(EncodeError::TooLong)
} else {
try!(self.write_byte(len as u8));
self.write(data)
}
}
pub fn write_name(&mut self, name: &str) -> Result<(), EncodeError> {
if !is_valid_name(name) {
Err(EncodeError::InvalidName)
} else if name == "." {
self.write_byte(0)
} else {
let mut total_len = 0;
for seg in name.split('.') {
let seg = match idna::to_ascii(seg) {
Ok(seg) => seg,
Err(_) => return Err(EncodeError::InvalidName)
};
if !is_valid_segment(&seg) {
return Err(EncodeError::InvalidName);
}
if seg.len() > LABEL_LIMIT {
return Err(EncodeError::InvalidName);
}
total_len += 1 + seg.len();
if total_len > NAME_LIMIT {
return Err(EncodeError::InvalidName);
}
try!(self.write_byte(seg.len() as u8));
try!(self.write(seg.as_bytes()));
}
if !name.ends_with('.') {
if total_len + 1 > NAME_LIMIT {
return Err(EncodeError::InvalidName);
}
try!(self.write_byte(0));
}
Ok(())
}
}
pub fn write_byte(&mut self, data: u8) -> Result<(), EncodeError> {
self.write(&[data])
}
pub fn write_u16(&mut self, data: u16) -> Result<(), EncodeError> {
let data: [u8; 2] = unsafe { transmute(data.to_be()) };
self.write(&data)
}
pub fn write_u32(&mut self, data: u32) -> Result<(), EncodeError> {
let data: [u8; 4] = unsafe { transmute(data.to_be()) };
self.write(&data)
}
fn write_header(&mut self, header: &FullHeader) -> Result<(), EncodeError> {
let mut hdr: HeaderData = unsafe { zeroed() };
hdr.id = header.id.to_be();
hdr.flags0 |= (header.qr as u8 & 1) << 7;
hdr.flags0 |= (header.op.to_u8() & 0b1111) << 3;
hdr.flags0 |= (header.authoritative as u8) << 2;
hdr.flags0 |= (header.truncated as u8) << 1;
hdr.flags0 |= header.recursion_desired as u8;
hdr.flags1 |= (header.recursion_available as u8) << 7;
hdr.flags1 |= header.rcode.to_u8() & 0b1111;
hdr.qd_count = header.qd_count.to_be();
hdr.an_count = header.an_count.to_be();
hdr.ns_count = header.ns_count.to_be();
hdr.ar_count = header.ar_count.to_be();
let buf: [u8; 12] = unsafe { transmute(hdr) };
self.write(&buf)
}
fn write_question(&mut self, question: &Question) -> Result<(), EncodeError> {
try!(self.write_name(&question.name));
let mut qd: QuestionData = unsafe { zeroed() };
qd.q_type = question.q_type.to_u16().to_be();
qd.q_class = question.q_class.to_u16().to_be();
let buf: [u8; 4] = unsafe { transmute(qd) };
self.write(&buf)
}
fn write_resource(&mut self, resource: &Resource) -> Result<(), EncodeError> {
try!(self.write_name(&resource.name));
let mut rd: ResourceData = unsafe { zeroed() };
let rdata = resource.get_rdata();
rd.r_type = resource.r_type.to_u16().to_be();
rd.r_class = resource.r_class.to_u16().to_be();
rd.ttl = resource.ttl.to_be();
rd.length = try!(to_u16(rdata.len())).to_be();
let buf: [u8; 10] = unsafe { transmute(rd) };
try!(self.write(&buf));
self.write(rdata)
}
}
pub fn generate_id() -> u16 {
thread_local!(static ID: Cell<u16> = Cell::new(random()));
ID.with(|id| {
let value = id.get();
id.set(value.wrapping_add(1));
value
})
}
fn is_valid_name(name: &str) -> bool {
let len = name.len();
len != 0 && (len == 1 || !name.starts_with('.')) && !name.contains("..")
}
fn is_valid_segment(s: &str) -> bool {
!(s.starts_with('-') || s.ends_with('-')) &&
s.chars().all(|c| !(c == '.' || c.is_whitespace() || c.is_control()))
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Message<'a> {
pub header: Header,
pub question: Vec<Question>,
pub answer: Vec<Resource<'a>>,
pub authority: Vec<Resource<'a>>,
pub additional: Vec<Resource<'a>>,
}
impl<'a> Message<'a> {
pub fn new() -> Message<'a> {
Message{
header: Header::new(),
..Default::default()
}
}
pub fn with_id(id: u16) -> Message<'a> {
Message{
header: Header::with_id(id),
..Default::default()
}
}
pub fn decode(data: &[u8]) -> Result<Message, DecodeError> {
let mut r = MsgReader::new(data);
let header = try!(r.read_header());
let mut msg = Message{
header: header.to_header(),
question: Vec::with_capacity(header.qd_count as usize),
answer: Vec::with_capacity(header.an_count as usize),
authority: Vec::with_capacity(header.ns_count as usize),
additional: Vec::with_capacity(header.ar_count as usize),
};
for _ in 0..header.qd_count {
msg.question.push(try!(r.read_question()));
}
for _ in 0..header.an_count {
msg.answer.push(try!(r.read_resource()));
}
for _ in 0..header.ns_count {
msg.authority.push(try!(r.read_resource()));
}
for _ in 0..header.ar_count {
msg.additional.push(try!(r.read_resource()));
}
try!(r.finish());
Ok(msg)
}
pub fn encode<'buf>(&self, buf: &'buf mut [u8]) -> Result<&'buf [u8], EncodeError> {
let mut w = MsgWriter::new(buf);
let hdr = &self.header;
let header = FullHeader{
id: hdr.id,
qr: hdr.qr,
op: hdr.op,
authoritative: hdr.authoritative,
truncated: hdr.truncated,
recursion_desired: hdr.recursion_desired,
recursion_available: hdr.recursion_available,
rcode: hdr.rcode,
qd_count: try!(to_u16(self.question.len())),
an_count: try!(to_u16(self.answer.len())),
ns_count: try!(to_u16(self.authority.len())),
ar_count: try!(to_u16(self.additional.len())),
};
try!(w.write_header(&header));
for q in &self.question {
try!(w.write_question(q));
}
for r in &self.answer {
try!(w.write_resource(r));
}
for r in &self.authority {
try!(w.write_resource(r));
}
for r in &self.additional {
try!(w.write_resource(r));
}
Ok(w.into_bytes())
}
pub fn get_error(&self) -> Result<(), DnsError> {
if self.header.rcode == RCode::NoError {
Ok(())
} else {
Err(DnsError(self.header.rcode))
}
}
pub fn records(&self) -> RecordIter {
RecordIter{
iters: [
self.answer.iter(),
self.authority.iter(),
self.additional.iter(),
]
}
}
pub fn into_records(self) -> RecordIntoIter<'a> {
RecordIntoIter{
iters: [
self.answer.into_iter(),
self.authority.into_iter(),
self.additional.into_iter(),
]
}
}
}
pub struct RecordIter<'a> {
iters: [Iter<'a, Resource<'a>>; 3],
}
impl<'a> Iterator for RecordIter<'a> {
type Item = &'a Resource<'a>;
fn next(&mut self) -> Option<&'a Resource<'a>> {
self.iters[0].next()
.or_else(|| self.iters[1].next())
.or_else(|| self.iters[2].next())
}
}
pub struct RecordIntoIter<'a> {
iters: [IntoIter<Resource<'a>>; 3],
}
impl<'a> Iterator for RecordIntoIter<'a> {
type Item = Resource<'a>;
fn next(&mut self) -> Option<Resource<'a>> {
self.iters[0].next()
.or_else(|| self.iters[1].next())
.or_else(|| self.iters[2].next())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Header {
pub id: u16,
pub qr: Qr,
pub op: OpCode,
pub authoritative: bool,
pub truncated: bool,
pub recursion_desired: bool,
pub recursion_available: bool,
pub rcode: RCode,
}
impl Header {
pub fn new() -> Header {
Header{
id: generate_id(),
..Default::default()
}
}
pub fn with_id(id: u16) -> Header {
Header{
id: id,
..Default::default()
}
}
}
impl Default for Header {
fn default() -> Header {
Header{
id: 0,
qr: Qr::Query,
op: OpCode::Query,
authoritative: false,
truncated: false,
recursion_desired: false,
recursion_available: false,
rcode: RCode::NoError,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct FullHeader {
pub id: u16,
pub qr: Qr,
pub op: OpCode,
pub authoritative: bool,
pub truncated: bool,
pub recursion_desired: bool,
pub recursion_available: bool,
pub rcode: RCode,
pub qd_count: u16,
pub an_count: u16,
pub ns_count: u16,
pub ar_count: u16,
}
impl FullHeader {
fn to_header(&self) -> Header {
Header{
id: self.id,
qr: self.qr,
op: self.op,
authoritative: self.authoritative,
truncated: self.truncated,
recursion_desired: self.recursion_desired,
recursion_available: self.recursion_available,
rcode: self.rcode,
}
}
}
impl Default for FullHeader {
fn default() -> FullHeader {
FullHeader{
id: 0,
qr: Qr::Query,
op: OpCode::Query,
authoritative: false,
truncated: false,
recursion_desired: false,
recursion_available: false,
rcode: RCode::NoError,
qd_count: 0,
an_count: 0,
ns_count: 0,
ar_count: 0,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Question {
pub name: String,
pub q_type: RecordType,
pub q_class: Class,
}
impl Question {
pub fn new(name: String, q_type: RecordType, q_class: Class) -> Question {
Question{
name: name,
q_type: q_type,
q_class: q_class,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Resource<'a> {
pub name: String,
pub r_type: RecordType,
pub r_class: Class,
pub ttl: u32,
data: Cow<'a, [u8]>,
offset: usize,
}
impl<'a> Resource<'a> {
pub fn new(name: String, r_type: RecordType,
r_class: Class, ttl: u32) -> Resource<'a> {
Resource{
name: name,
r_type: r_type,
r_class: r_class,
ttl: ttl,
data: Owned(Vec::new()),
offset: 0,
}
}
pub fn get_rdata(&self) -> &[u8] {
&self.data[self.offset..]
}
pub fn read_rdata<R: Record>(&self) -> Result<R, DecodeError> {
let mut r = MsgReader::with_offset(&self.data, self.offset);
let res = try!(Record::decode(&mut r));
try!(r.finish());
Ok(res)
}
pub fn write_rdata<R: Record>(&mut self, record: &R) -> Result<(), EncodeError> {
let mut buf = [0; MESSAGE_LIMIT];
let mut w = MsgWriter::new(&mut buf[..]);
try!(record.encode(&mut w));
self.data = Owned(w.into_bytes().to_vec());
self.offset = 0;
Ok(())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Qr {
Query = 0,
Response = 1,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OpCode {
Query,
Status,
Notify,
Update,
Other(u8),
}
impl OpCode {
pub fn from_u8(u: u8) -> OpCode {
match u {
0 => OpCode::Query,
2 => OpCode::Status,
4 => OpCode::Notify,
5 => OpCode::Update,
n => OpCode::Other(n),
}
}
pub fn to_u8(&self) -> u8 {
match *self {
OpCode::Query => 0,
OpCode::Status => 2,
OpCode::Notify => 4,
OpCode::Update => 5,
OpCode::Other(n) => n,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RCode {
NoError,
FormatError,
ServerFailure,
NameError,
NotImplemented,
Refused,
Other(u8),
}
impl RCode {
pub fn get_error(&self) -> &'static str {
match *self {
RCode::NoError => "no error",
RCode::FormatError => "format error",
RCode::ServerFailure => "server failure",
RCode::NameError => "no such name",
RCode::NotImplemented => "not implemented",
RCode::Refused => "refused",
RCode::Other(_) => "unknown response code",
}
}
pub fn from_u8(u: u8) -> RCode {
match u {
0 => RCode::NoError,
1 => RCode::FormatError,
2 => RCode::ServerFailure,
3 => RCode::NameError,
4 => RCode::NotImplemented,
5 => RCode::Refused,
n => RCode::Other(n),
}
}
pub fn to_u8(&self) -> u8 {
match *self {
RCode::NoError => 0,
RCode::FormatError => 1,
RCode::ServerFailure => 2,
RCode::NameError => 3,
RCode::NotImplemented => 4,
RCode::Refused => 5,
RCode::Other(n) => n,
}
}
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
struct HeaderData {
id: u16,
flags0: u8,
flags1: u8,
qd_count: u16,
an_count: u16,
ns_count: u16,
ar_count: u16,
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
struct QuestionData {
q_type: u16,
q_class: u16,
}
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
struct ResourceData {
r_type: u16,
r_class: u16,
ttl: u32,
length: u16,
}
fn to_u16(n: usize) -> Result<u16, EncodeError> {
if n > u16::max_value() as usize {
Err(EncodeError::TooLong)
} else {
Ok(n as u16)
}
}
#[cfg(test)]
mod test {
use super::{is_valid_name, EncodeError, MESSAGE_LIMIT};
use super::{Header, Message, Question, Qr, OpCode, RCode};
use super::{MsgReader, MsgWriter};
use record::{Class, RecordType};
#[test]
fn test_idna_name() {
let mut buf = [0; 64];
let mut w = MsgWriter::new(&mut buf);
w.write_name("bücher.de.").unwrap();
w.write_name("ουτοπία.δπθ.gr.").unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes, &b"\
\x0dxn--bcher-kva\x02de\x00\
\x0exn--kxae4bafwg\x09xn--pxaix\x02gr\x00\
"[..]);
let mut r = MsgReader::new(&bytes);
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok("bücher.de."));
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok("ουτοπία.δπθ.gr."));
}
#[test]
fn test_message() {
let msg = Message{
header: Header{
id: 0xabcd,
qr: Qr::Query,
op: OpCode::Query,
authoritative: false,
truncated: false,
recursion_desired: true,
recursion_available: true,
rcode: RCode::NoError,
},
question: vec![
Question::new("foo.bar.com.".to_owned(),
RecordType::A, Class::Internet)
],
answer: Vec::new(),
authority: Vec::new(),
additional: Vec::new(),
};
let mut buf = [0; 64];
let bytes = msg.encode(&mut buf).unwrap();
assert_eq!(bytes,
&[0xab, 0xcd,
0b00000001, 0b10000000,
0, 1, 0, 0, 0, 0, 0, 0,
3, b'f', b'o', b'o',
3, b'b', b'a', b'r',
3, b'c', b'o', b'm', 0,
0, 1, 0, 1][..]);
let msg2 = Message::decode(&bytes).unwrap();
assert_eq!(msg, msg2);
}
#[test]
fn test_primitives() {
let mut buf = [0; 64];
let mut w = MsgWriter::new(&mut buf);
w.write_byte(0x11).unwrap();
w.write_u16(0x2233).unwrap();
w.write_u32(0x44556677).unwrap();
w.write_name("alpha.bravo.charlie").unwrap();
w.write_name("delta.echo.foxtrot.").unwrap();
w.write_name(".").unwrap();
assert_eq!(w.write_name(""), Err(EncodeError::InvalidName));
assert_eq!(w.write_name(
"ohmyglobhowdidthisgethereiamnotgoodwithcomputerrrrrrrrrrrrrrrrrr.org"),
Err(EncodeError::InvalidName));
let bytes = w.into_bytes();
assert_eq!(bytes, &b"\
\x11\
\x22\x33\
\x44\x55\x66\x77\
\x05alpha\x05bravo\x07charlie\x00\
\x05delta\x04echo\x07foxtrot\x00\
\x00"[..]);
let mut r = MsgReader::new(&bytes);
assert_eq!(r.read_byte(), Ok(0x11));
assert_eq!(r.read_u16(), Ok(0x2233));
assert_eq!(r.read_u32(), Ok(0x44556677));
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok("alpha.bravo.charlie."));
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok("delta.echo.foxtrot."));
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok("."));
}
const LONGEST_NAME: &'static str =
"aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaa\
.com";
const LONGEST_NAME_DOT: &'static str =
"aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaa\
.com.";
const TOO_LONG_NAME: &'static str =
"aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
.com";
const TOO_LONG_NAME_DOT: &'static str =
"aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaa\
.com.";
const TOO_LONG_SEGMENT: &'static str =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaa.com";
#[test]
fn test_encode_name() {
let mut buf = [0; MESSAGE_LIMIT];
let mut w = MsgWriter::new(&mut buf);
w.write_name(LONGEST_NAME).unwrap();
w.write_name(LONGEST_NAME_DOT).unwrap();
let bytes = w.into_bytes();
let mut r = MsgReader::new(&bytes);
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok(LONGEST_NAME_DOT));
assert_eq!(r.read_name().as_ref().map(|s| &s[..]), Ok(LONGEST_NAME_DOT));
let mut buf = [0; MESSAGE_LIMIT];
let mut w = MsgWriter::new(&mut buf);
assert_eq!(w.write_name(TOO_LONG_NAME), Err(EncodeError::InvalidName));
assert_eq!(w.write_name(TOO_LONG_NAME_DOT), Err(EncodeError::InvalidName));
assert_eq!(w.write_name(TOO_LONG_SEGMENT), Err(EncodeError::InvalidName));
}
#[test]
fn test_valid_name() {
assert!(is_valid_name("."));
assert!(is_valid_name("foo.com."));
assert!(is_valid_name("foo-123.com."));
assert!(is_valid_name("FOO-BAR.COM"));
assert!(!is_valid_name(""));
assert!(!is_valid_name(".foo.com"));
assert!(!is_valid_name("foo..bar.com"));
}
}