#![cfg(all(feature = "tsig", feature = "bytes", feature = "std"))]
mod common;
use std::io::{Read, Write};
use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, UdpSocket};
use std::process::Command;
use std::str::FromStr;
use std::string::String;
use std::time::Duration;
use std::vec::Vec;
use std::{env, fs, io, path::PathBuf, thread};
use domain::base::iana::{Rcode, Rtype};
use domain::base::message::Message;
use domain::base::message_builder::{
AdditionalBuilder, AnswerBuilder, MessageBuilder, StreamTarget,
};
use domain::base::name::Name;
use domain::base::opt::TcpKeepalive;
use domain::base::record::Ttl;
use domain::rdata::tsig::Time48;
use domain::rdata::{Soa, A};
use domain::tsig;
use domain::utils::base64;
use ring::rand::SystemRandom;
use common::nsd;
type TestMessage = Message<Vec<u8>>;
type TestBuilder = MessageBuilder<StreamTarget<Vec<u8>>>;
type TestAnswer = AnswerBuilder<StreamTarget<Vec<u8>>>;
type TestAdditional = AdditionalBuilder<StreamTarget<Vec<u8>>>;
#[test]
#[ignore]
fn tsig_client_nsd() {
let rng = SystemRandom::new();
let cur_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let base_dir = cur_dir.join("target/test/tsig_client_nsd");
fs::create_dir_all(&base_dir).unwrap();
let base_dir = base_dir.canonicalize().unwrap();
let nsdconfpath = base_dir.join("nsd.conf");
let zonepath = cur_dir.join("test-data/zonefiles/example.com.txt");
let (key, secret) = tsig::Key::generate(
tsig::Algorithm::Sha1,
&rng,
Name::from_str("test.key.").unwrap(),
None,
None,
)
.unwrap();
let mut conf = nsd::Config::all_in(&base_dir);
conf.ip_address
.push(SocketAddr::from_str("127.0.0.1:54321").unwrap());
conf.verbosity = Some(3);
conf.keys
.push(nsd::KeyConfig::new("test.key.", "hmac-sha1", secret));
conf.zones.push(nsd::ZoneConfig::new(
"example.com",
zonepath,
vec![nsd::Acl::new(
IpAddr::from_str("127.0.0.1").unwrap(),
None,
None,
Some("test.key.".into()),
)],
));
conf.save(&nsdconfpath).unwrap();
let mut nsd = Command::new("/usr/sbin/nsd")
.args(["-c", &format!("{}", nsdconfpath.display()), "-d"])
.spawn()
.expect("failed to start nsd");
thread::sleep(Duration::from_secs(1));
if nsd.try_wait().unwrap().is_some() {
panic!("NSD didn't start.");
}
let res = thread::spawn(move || {
let request = TestBuilder::new_stream_vec();
let mut request = request
.request_axfr(Name::<Vec<u8>>::from_str("example.com.").unwrap())
.unwrap()
.additional();
request
.opt(|builder| builder.push(&TcpKeepalive::new(None)))
.unwrap();
let tran = tsig::ClientTransaction::request(
&key,
&mut request,
Time48::now(),
)
.unwrap();
let sock = UdpSocket::bind("127.0.0.1:54320").unwrap();
sock.send_to(request.as_target().as_dgram_slice(), "127.0.0.1:54321")
.unwrap();
let mut answer = loop {
let mut buf = vec![0; 512];
let (len, addr) = sock.recv_from(buf.as_mut()).unwrap();
if addr != SocketAddr::from_str("127.0.0.1:54321").unwrap() {
continue;
}
buf.truncate(len);
let answer = Message::from_octets(buf).unwrap();
if answer.header().id() == request.header().id() {
break answer;
}
};
assert_eq!(answer.header().rcode(), Rcode::NOTIMP);
if let Err(err) = tran.answer(&mut answer, Time48::now()) {
panic!("{err:?}");
}
})
.join();
let _ = nsd.kill();
res.unwrap(); }
#[test]
#[ignore]
fn tsig_server_dig() {
let rng = SystemRandom::new();
let (key, secret) = tsig::Key::generate(
tsig::Algorithm::Sha1,
&rng,
Name::from_str("test.key.").unwrap(),
None,
None,
)
.unwrap();
let secret = base64::encode_string(&secret);
let secret = format!("hmac-sha1:test.key:{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();
buf.truncate(len);
let mut request = match Message::from_octets(buf) {
Ok(request) => request,
Err(_) => continue,
};
let answer = TestBuilder::new_stream_vec();
let answer =
answer.start_answer(&request, Rcode::NOERROR).unwrap();
let tran = match tsig::ServerTransaction::request(
&&key,
&mut request,
Time48::now(),
) {
Ok(Some(tran)) => tran,
Ok(None) => {
sock.send_to(answer.as_slice(), addr).unwrap();
continue;
}
Err(error) => {
let answer = error
.build_message(
&request,
TestBuilder::new_stream_vec(),
)
.unwrap();
sock.send_to(answer.as_slice(), addr).unwrap();
continue;
}
};
let mut answer = answer.additional();
tran.answer(&mut answer, Time48::now()).unwrap();
sock.send_to(answer.as_slice(), addr).unwrap();
}
});
let output = Command::new("/usr/bin/dig")
.args(["-p", "54322", "-y", &secret, "example.com", "@127.0.0.1"])
.output()
.expect("failed to start dig");
drop(join);
assert!(output.status.success());
assert!(!String::from_utf8(output.stdout)
.unwrap()
.contains("tsig verify failure"));
}
#[test]
#[ignore]
fn tsig_client_sequence_nsd() {
let rng = SystemRandom::new();
let cur_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let base_dir = cur_dir.join("target/test/tsig_client_sequence_nsd");
fs::create_dir_all(&base_dir).unwrap();
let base_dir = base_dir.canonicalize().unwrap();
let nsdconfpath = base_dir.join("nsd.conf");
let zonepath = cur_dir.join("test-data/zonefiles/big.example.com.txt");
let (key, secret) = tsig::Key::generate(
tsig::Algorithm::Sha1,
&rng,
Name::from_str("test.key.").unwrap(),
None,
None,
)
.unwrap();
let mut conf = nsd::Config::all_in(&base_dir);
conf.ip_address
.push(SocketAddr::from_str("127.0.0.1:54323").unwrap());
conf.verbosity = Some(3);
conf.keys
.push(nsd::KeyConfig::new("test.key.", "hmac-sha1", secret));
conf.zones.push(nsd::ZoneConfig::new(
"example.com",
zonepath,
vec![nsd::Acl::new(
IpAddr::from_str("127.0.0.1").unwrap(),
None,
None,
Some("test.key.".into()),
)],
));
conf.save(&nsdconfpath).unwrap();
let mut nsd = Command::new("/usr/sbin/nsd")
.args(["-c", &format!("{}", nsdconfpath.display()), "-d"])
.spawn()
.expect("failed to start nsd");
thread::sleep(Duration::from_secs(1));
if nsd.try_wait().unwrap().is_some() {
panic!("NSD didn't start.");
}
let res = thread::spawn(move || {
let mut sock = TcpStream::connect("127.0.0.1:54323").unwrap();
let request = TestBuilder::new_stream_vec();
let mut request = request
.request_axfr(Name::<Vec<u8>>::from_str("example.com.").unwrap())
.unwrap()
.additional();
let mut tran =
tsig::ClientSequence::request(&key, &mut request, Time48::now())
.unwrap();
sock.write_all(request.as_target().as_stream_slice())
.unwrap();
loop {
let mut len = [0u8; 2];
sock.read_exact(&mut len).unwrap();
let len = u16::from_be_bytes(len) as usize;
assert!(len != 0);
let mut buf = vec![0; len];
sock.read_exact(&mut buf).unwrap();
let mut answer = Message::from_octets(buf).unwrap();
tran.answer(&mut answer, Time48::now()).unwrap();
if answer.answer().unwrap().last().unwrap().unwrap().rtype()
== Rtype::SOA
{
break;
}
}
tran.done().unwrap()
})
.join();
let _ = nsd.kill();
res.unwrap(); }
#[test]
#[ignore]
fn tsig_server_sequence_dig() {
let rng = SystemRandom::new();
let (key, secret) = tsig::Key::generate(
tsig::Algorithm::Sha1,
&rng,
Name::from_str("test.key.").unwrap(),
None,
None,
)
.unwrap();
let secret = base64::encode_string(&secret);
let secret = format!("hmac-sha1:test.key:{secret}");
let listener = TcpListener::bind("127.0.0.1:54324").unwrap();
let port = listener.local_addr().unwrap().port();
let join = thread::spawn(move || {
for sock in listener.incoming() {
let mut sock = sock.unwrap();
let mut buf = [0u8, 2];
sock.read_exact(&mut buf).unwrap();
let len = u16::from_be_bytes(buf) as usize;
let mut buf = vec![0; len];
sock.read_exact(&mut buf).unwrap();
let mut request = Message::from_octets(buf).unwrap();
let mut tran = tsig::ServerSequence::request(
&&key,
&mut request,
Time48::now(),
)
.unwrap()
.unwrap();
let mut answer = make_first_axfr(&request);
tran.answer(&mut answer, Time48::now()).unwrap();
send_tcp(&mut sock, answer.as_target().as_stream_slice())
.unwrap();
for two in 0..10u8 {
for one in 0..10u8 {
let mut answer = make_middle_axfr(&request, one, two);
tran.answer(&mut answer, Time48::now()).unwrap();
send_tcp(&mut sock, answer.as_target().as_stream_slice())
.unwrap();
}
}
let mut answer = make_last_axfr(&request);
tran.answer(&mut answer, Time48::now()).unwrap();
send_tcp(&mut sock, answer.as_target().as_stream_slice())
.unwrap();
}
});
let output = Command::new("/usr/bin/dig")
.args([
"-p",
&format!("{port}"),
"-y",
&secret,
"example.com",
"AXFR",
"@127.0.0.1",
"+tcp",
])
.output()
.expect("failed to start dig");
drop(join);
assert!(output.status.success());
assert!(!String::from_utf8(output.stdout)
.unwrap()
.contains("tsig verify failure"));
}
fn send_tcp(sock: &mut TcpStream, msg: &[u8]) -> Result<(), io::Error> {
sock.write_all(msg)?;
Ok(())
}
fn make_first_axfr(request: &TestMessage) -> TestAdditional {
let msg = TestBuilder::new_stream_vec();
let mut msg = msg.start_answer(request, Rcode::NOERROR).unwrap();
push_soa(&mut msg);
push_a(&mut msg, 0, 0, 0);
msg.additional()
}
fn make_middle_axfr(
request: &TestMessage,
one: u8,
two: u8,
) -> TestAdditional {
let msg = TestBuilder::new_stream_vec();
let mut msg = msg.start_answer(request, Rcode::NOERROR).unwrap();
push_a(&mut msg, 1, one, two);
msg.additional()
}
fn make_last_axfr(request: &TestMessage) -> TestAdditional {
let msg = TestBuilder::new_stream_vec();
let mut msg = msg.start_answer(request, Rcode::NOERROR).unwrap();
push_a(&mut msg, 2, 0, 0);
push_soa(&mut msg);
msg.additional()
}
fn push_soa(builder: &mut TestAnswer) {
builder
.push((
Name::<Vec<u8>>::from_str("example.com.").unwrap(),
3600,
Soa::new(
Name::<Vec<u8>>::from_str("mname.example.com.").unwrap(),
Name::<Vec<u8>>::from_str("rname.example.com.").unwrap(),
12.into(),
Ttl::from_secs(3600),
Ttl::from_secs(3600),
Ttl::from_secs(3600),
Ttl::from_secs(3600),
),
))
.unwrap()
}
fn push_a(builder: &mut TestAnswer, zero: u8, one: u8, two: u8) {
builder
.push((
Name::<Vec<u8>>::from_str("example.com.").unwrap(),
3600,
A::from_octets(10, zero, one, two),
))
.unwrap()
}