mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-09-28 12:44:59 +02:00
Add a test for the TSIG server transaction against drill.
This commit is contained in:
+1
-1
@@ -4,4 +4,4 @@ rust:
|
||||
- beta
|
||||
- nightly
|
||||
before_install:
|
||||
- sudo apt-get install -y nsd
|
||||
- sudo apt-get install -y nsd ldnsutils
|
||||
|
||||
@@ -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
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user