diff --git a/.travis.yml b/.travis.yml index 69508830..3a972621 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ rust: - beta - nightly before_install: - - sudo apt-get install -y nsd + - sudo apt-get install -y nsd ldnsutils diff --git a/domain-core/src/bits/message_builder.rs b/domain-core/src/bits/message_builder.rs index b13a3cc5..9f232873 100644 --- a/domain-core/src/bits/message_builder.rs +++ b/domain-core/src/bits/message_builder.rs @@ -285,27 +285,6 @@ impl MessageBuilder { pub fn set_page_size(&mut self, page_size: usize) { self.target.buf.set_page_size(page_size) } - - /// Starts creating an answer for the given message. - /// - /// Specifically, this sets the ID, QR, OPCODE, RD, and RCODE fields - /// in the header and attempts to push the message’s questions to the - /// builder. If iterating of the questions fails, it adds what it can. - pub fn start_answer(&mut self, msg: &Message, rcode: Rcode) { - { - let header = self.header_mut(); - header.set_id(msg.header().id()); - header.set_qr(true); - header.set_opcode(msg.header().opcode()); - header.set_rd(msg.header().rd()); - header.set_rcode(rcode); - } - for item in msg.question() { - if let Ok(item) = item { - self.push(item).unwrap(); - } - } - } } @@ -352,6 +331,28 @@ impl MessageBuilder { /// # Shortcuts /// impl MessageBuilder { + + /// Starts creating an answer for the given message. + /// + /// Specifically, this sets the ID, QR, OPCODE, RD, and RCODE fields + /// in the header and attempts to push the message’s questions to the + /// builder. If iterating of the questions fails, it adds what it can. + pub fn start_answer(&mut self, msg: &Message, rcode: Rcode) { + { + let header = self.header_mut(); + header.set_id(msg.header().id()); + header.set_qr(true); + header.set_opcode(msg.header().opcode()); + header.set_rd(msg.header().rd()); + header.set_rcode(rcode); + } + for item in msg.question() { + if let Ok(item) = item { + self.push(item).unwrap(); + } + } + } + /// Creates an AXFR request for the given domain. pub fn request_axfr(apex: N) -> Self { let mut res = Self::new_udp(); diff --git a/domain-core/src/tsig.rs b/domain-core/src/tsig.rs index e7ead997..0ef91068 100644 --- a/domain-core/src/tsig.rs +++ b/domain-core/src/tsig.rs @@ -231,6 +231,21 @@ pub trait KeyStore { ) -> Option; } +impl<'a> KeyStore for &'a Key { + type Key = &'a Key; + + fn get_key( + &self, name: &N, algorithm: Algorithm + ) -> Option { + if self.name() == name && self.algorithm() == algorithm { + Some(*self) + } + else { + None + } + } +} + impl KeyStore for HashMap<(Dname, Algorithm), Arc> { type Key = Arc; @@ -472,20 +487,49 @@ impl> ServerTransaction { // Note that we are not doing the caching of the most recent // time_signed because, well, that’ll require mutexes and stuff. if !time_valid { - let mut tran = tran; - tran.variables.other = Some(Time48::now()); - tran.variables.error = TsigRcode::BadTime; let mut response = MessageBuilder::new_udp(); response.start_answer(&message, Rcode::NotAuth); return Err( // unwrap: answer should always fit. - tran.signed_answer(response.additional()).unwrap() + tran.signed_answer( + response.additional(), + &Variables::new( + tran.variables.time_signed, + tran.variables.fudge, + TsigRcode::BadTime, + Some(Time48::now()) + ) + ).unwrap() ) } Ok((message, Some(tran))) } + /// Produces a signed answer. + pub fn answer( + &self, message: AdditionalBuilder + ) -> Result { + self.answer_with_fudge(message, 300) + } + + /// Produces a signed answer with a given fudge. + pub fn answer_with_fudge( + &self, + message: AdditionalBuilder, + fudge: u16 + ) -> Result { + self.signed_answer( + message, + &Variables::new( + Time48::now(), + fudge, + TsigRcode::NoError, + None + ) + ) + } + /// Produces an unsigned error answer. fn unsigned_answer( msg: &Message, @@ -514,13 +558,14 @@ impl> ServerTransaction { fn signed_answer( &self, mut message: AdditionalBuilder, + variables: &Variables, ) -> Result { let id = message.header().id(); - let mac = self.variables.sign_answer( + let mac = variables.sign_answer( self.key(), &self.request_mac, message.so_far() ); let mac = Signature::local(mac, self.key().signing_len); - message.push(self.variables.to_tsig(self.key(), &mac, id))?; + message.push(variables.to_tsig(self.key(), &mac, id))?; Ok(message.freeze()) } } diff --git a/domain-core/src/utils/base64.rs b/domain-core/src/utils/base64.rs index 33785d03..f7ffe37d 100644 --- a/domain-core/src/utils/base64.rs +++ b/domain-core/src/utils/base64.rs @@ -48,6 +48,13 @@ where B: AsRef<[u8]> + ?Sized, W: fmt::Write { } +pub fn encode_string + ?Sized>(bytes: &B) -> String { + let mut res = String::with_capacity((bytes.as_ref().len() / 3 + 1) * 4); + display(bytes, &mut res).unwrap(); + res +} + + //------------ Decoder ------------------------------------------------------- /// A Base64 decoder. diff --git a/interop/tests/tsig.rs b/interop/tests/tsig.rs index 3453f97c..477327bd 100644 --- a/interop/tests/tsig.rs +++ b/interop/tests/tsig.rs @@ -10,10 +10,13 @@ use std::time::Duration; use ring::rand::SystemRandom; use interop::nsd; use interop::domain::core::bits::{Dname, Message, MessageBuilder}; +use interop::domain::core::bits::message_builder::SectionBuilder; +use interop::domain::core::iana::Rcode; +use interop::domain::core::utils::base64; use interop::domain::core::tsig; -/// Tests the TSIG client implementation agains NSD as a server. +/// Tests the TSIG client implementation against NSD as a server. /// /// Spins up an NSD serving example.com. and then tries to AXFR that. #[test] @@ -83,3 +86,57 @@ fn tsig_client_nsd() { // Shut down NSD just to be sure. let _ = nsd.kill(); } + +/// Tests the TSIG server implementation against drill as a client. +#[test] +fn tsig_server_drill() { + let rng = SystemRandom::new(); + let (key, secret) = tsig::Key::generate( + tsig::Algorithm::Sha1, + &rng, + Dname::from_str("test.key.").unwrap(), + None, + None + ).unwrap(); + let secret = base64::encode_string(&secret); + let secret = format!("test.key:{}:hmac-sha1", secret); + + let join = thread::spawn(move || { + let sock = UdpSocket::bind("127.0.0.1:54322").unwrap(); + loop { + let mut buf = vec![0; 512]; + let (len, addr) = sock.recv_from(buf.as_mut()).unwrap(); + let request = match Message::from_bytes(buf[..len].into()) { + Ok(request) => request, + Err(_) => continue, + }; + let mut answer = MessageBuilder::new_udp(); + answer.start_answer(&request, Rcode::NoError); + let (_msg, tran) = match tsig::ServerTransaction::request(&&key, + request) { + Ok((msg, Some(tran))) => (msg, tran), + Ok((_, None)) => { + sock.send_to(answer.freeze().as_slice(), addr).unwrap(); + continue; + } + Err(error) => { + sock.send_to(error.as_slice(), addr).unwrap(); + continue; + } + }; + let answer = tran.answer(answer.additional()).unwrap(); + sock.send_to(answer.as_slice(), addr).unwrap(); + } + }); + + let status = Command::new("/usr/bin/drill") + .args(&[ + "-p", "54322", + "-y", &secret, + "example.com", "@127.0.0.1" + ]) + .status().unwrap(); + drop(join); + assert!(status.success()); +} +