summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dns-parser/src/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/dns-parser/src/builder.rs')
-rw-r--r--third_party/rust/dns-parser/src/builder.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/third_party/rust/dns-parser/src/builder.rs b/third_party/rust/dns-parser/src/builder.rs
new file mode 100644
index 0000000000..22aff0b962
--- /dev/null
+++ b/third_party/rust/dns-parser/src/builder.rs
@@ -0,0 +1,132 @@
+use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
+
+use {Opcode, ResponseCode, Header, QueryType, QueryClass};
+
+/// Allows to build a DNS packet
+///
+/// Both query and answer packets may be built with this interface, although,
+/// much of functionality is not implemented yet.
+#[derive(Debug)]
+pub struct Builder {
+ buf: Vec<u8>,
+}
+
+impl Builder {
+ /// Creates a new query
+ ///
+ /// Initially all sections are empty. You're expected to fill
+ /// the questions section with `add_question`
+ 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 }
+ }
+ /// Adds a question to the packet
+ ///
+ /// # Panics
+ ///
+ /// * Answers, nameservers or additional section has already been written
+ /// * There are already 65535 questions in the buffer.
+ /// * When name is invalid
+ 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);
+ }
+ /// Returns the final packet
+ ///
+ /// When packet is not truncated method returns `Ok(packet)`. If
+ /// packet is truncated the method returns `Err(packet)`. In both
+ /// cases the packet is fully valid.
+ ///
+ /// In the server implementation you may use
+ /// `x.build().unwrap_or_else(|x| x)`.
+ ///
+ /// In the client implementation it's probably unwise to send truncated
+ /// packet, as it doesn't make sense. Even panicking may be more
+ /// appropriate.
+ // TODO(tailhook) does the truncation make sense for TCP, and how
+ // to treat it for EDNS0?
+ pub fn build(mut self) -> Result<Vec<u8>,Vec<u8>> {
+ // TODO(tailhook) optimize labels
+ 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[..]);
+ }
+}