extern crate eyre;
use std::io::{stdin, stdout, Read, Write};
use nonymous::{
core::{Class, Opcode, Rcode, Type},
emit::name::NameBuilder,
server::response,
};
fn main() -> eyre::Result<()> {
let mut query = vec![];
stdin().lock().read_to_end(&mut query)?;
let response = rdns_response(&query)?;
stdout().lock().write_all(&response)?;
Ok(())
}
fn rdns_response(query: &[u8]) -> eyre::Result<Vec<u8>> {
let mut result = vec![];
if let Some((query, response)) = response(&query, &mut result, false)? {
if query.header().opcode() != Opcode::Query || query.header().qdcount() != 1 {
response.rcode(Rcode::NotImp).finish()?.finish();
return Ok(result);
}
let question = query.qd().next().unwrap();
if question.qclass() != Class::IN || question.qtype() != Type::PTR {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
let qname = question.qname();
let labels = qname
.labels_not_null()
.flat_map(|x| x.value())
.collect::<Vec<_>>();
let name = match labels[..] {
[.., b"in-addr", b"arpa"] => {
if labels.len() != 4 + 2 {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
let octets = labels
.iter()
.take(4)
.rev()
.flat_map(|x| std::str::from_utf8(x))
.flat_map(|x| x.parse::<u8>())
.map(|x| x.to_string())
.collect::<Vec<_>>();
if octets.len() != 4 {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
let result = octets.join("-");
format!("dyn-{}.isp.example.", result)
}
[.., b"ip6", b"arpa"] => {
if labels.len() != 32 + 2 {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
let nibbles = labels
.iter()
.take(32)
.rev()
.flat_map(|x| std::str::from_utf8(x))
.flat_map(|x| u16::from_str_radix(x, 16))
.filter(|&x| x <= 0xF)
.collect::<Vec<_>>();
if nibbles.len() != 32 {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
let result = nibbles
.chunks_exact(4)
.map(|x| x.iter().fold(0, |a, x| a << 4 | x))
.map(|x| format!("{:x}", x))
.collect::<Vec<_>>()
.join("-");
format!("dyn-{}.isp.example.", result)
}
_ => {
response.rcode(Rcode::Refused).finish()?.finish();
return Ok(result);
}
};
let mut response = response
.rcode(Rcode::NoError)
.copy_question(&question)?
.into_an()
.record()?
.name()?;
for label in &labels {
response = response.label(label)?;
}
response
.try_into_rdata()?
.class(Class::IN)
.r#type(Type::PTR)
.build_rdata::<NameBuilder<_>>()?
.labels(name.as_bytes())?
.return_to_rdata()?
.finish()?
.finish()?
.finish();
};
Ok(result)
}