Add a test for the TSIG server transaction against drill.

This commit is contained in:
Martin Hoffmann
2019-03-25 16:15:46 +01:00
parent c7f8de62a8
commit 94beea3728
5 changed files with 139 additions and 29 deletions
+1 -1
View File
@@ -4,4 +4,4 @@ rust:
- beta
- nightly
before_install:
- sudo apt-get install -y nsd
- sudo apt-get install -y nsd ldnsutils
+22 -21
View File
@@ -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<N: ToDname>(apex: N) -> Self {
let mut res = Self::new_udp();
+51 -6
View File
@@ -231,6 +231,21 @@ pub trait KeyStore {
) -> Option<Self::Key>;
}
impl<'a> KeyStore for &'a Key {
type Key = &'a Key;
fn get_key<N: ToDname>(
&self, name: &N, algorithm: Algorithm
) -> Option<Self::Key> {
if self.name() == name && self.algorithm() == algorithm {
Some(*self)
}
else {
None
}
}
}
impl KeyStore for HashMap<(Dname, Algorithm), Arc<Key>> {
type Key = Arc<Key>;
@@ -472,20 +487,49 @@ impl<K: AsRef<Key>> ServerTransaction<K> {
// 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<Message, ShortBuf> {
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<Message, ShortBuf> {
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<K: AsRef<Key>> ServerTransaction<K> {
fn signed_answer(
&self,
mut message: AdditionalBuilder,
variables: &Variables,
) -> Result<Message, ShortBuf> {
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())
}
}
+7
View File
@@ -48,6 +48,13 @@ where B: AsRef<[u8]> + ?Sized, W: fmt::Write {
}
pub fn encode_string<B: AsRef<[u8]> + ?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.
+58 -1
View File
@@ -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());
}