use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
use {Opcode, ResponseCode, Header, QueryType, QueryClass};
#[derive(Debug)]
pub struct Builder {
buf: Vec<u8>,
}
impl Builder {
pub fn new_query(id: u16, recursion: bool) -> Builder {
let mut buf = Vec::with_capacity(512);
let head = Header {
id: id,
query: true,
opcode: Opcode::StandardQuery,
authoritative: false,
truncated: false,
recursion_desired: recursion,
recursion_available: false,
authenticated_data: false,
checking_disabled: false,
response_code: ResponseCode::NoError,
questions: 0,
answers: 0,
nameservers: 0,
additional: 0,
};
buf.extend([0u8; 12].iter());
head.write(&mut buf[..12]);
Builder { buf: buf }
}
pub fn add_question(&mut self, qname: &str, prefer_unicast: bool,
qtype: QueryType, qclass: QueryClass)
-> &mut Builder
{
if &self.buf[6..12] != b"\x00\x00\x00\x00\x00\x00" {
panic!("Too late to add a question");
}
self.write_name(qname);
self.buf.write_u16::<BigEndian>(qtype as u16).unwrap();
let prefer_unicast: u16 = if prefer_unicast { 0x8000 } else { 0x0000 };
self.buf.write_u16::<BigEndian>(qclass as u16 | prefer_unicast).unwrap();
let oldq = BigEndian::read_u16(&self.buf[4..6]);
if oldq == 65535 {
panic!("Too many questions");
}
BigEndian::write_u16(&mut self.buf[4..6], oldq+1);
self
}
fn write_name(&mut self, name: &str) {
for part in name.split('.') {
assert!(part.len() < 63);
let ln = part.len() as u8;
self.buf.push(ln);
self.buf.extend(part.as_bytes());
}
self.buf.push(0);
}
pub fn build(mut self) -> Result<Vec<u8>,Vec<u8>> {
if self.buf.len() > 512 {
Header::set_truncated(&mut self.buf[..12]);
Err(self.buf)
} else {
Ok(self.buf)
}
}
}
#[cfg(test)]
mod test {
use QueryType as QT;
use QueryClass as QC;
use super::Builder;
#[test]
fn build_query() {
let mut bld = Builder::new_query(1573, true);
bld.add_question("example.com", false, QT::A, QC::IN);
let result = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
\x07example\x03com\x00\x00\x01\x00\x01";
assert_eq!(&bld.build().unwrap()[..], &result[..]);
}
#[test]
fn build_unicast_query() {
let mut bld = Builder::new_query(1573, true);
bld.add_question("example.com", true, QT::A, QC::IN);
let result = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
\x07example\x03com\x00\x00\x01\x80\x01";
assert_eq!(&bld.build().unwrap()[..], &result[..]);
}
#[test]
fn build_srv_query() {
let mut bld = Builder::new_query(23513, true);
bld.add_question("_xmpp-server._tcp.gmail.com", false, QT::SRV, QC::IN);
let result = b"[\xd9\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
\x0c_xmpp-server\x04_tcp\x05gmail\x03com\x00\x00!\x00\x01";
assert_eq!(&bld.build().unwrap()[..], &result[..]);
}
}