use futures::{future, Future};
use std::{
net::{SocketAddr, TcpListener, UdpSocket},
pin::Pin,
str::FromStr,
sync::Arc,
time::Duration,
};
use tokio::runtime::Runtime;
use trust_dns_client::{
op::{LowerQuery, ResponseCode},
rr::{
dnssec::{DnsSecResult, Signer, SupportedAlgorithms},
rdata::key::KEY,
LowerName, Name, RData, Record, RecordType,
},
};
use trust_dns_server::{
authority::{
AuthLookup, Authority, Catalog, LookupError, LookupRecords, LookupResult, MessageRequest,
UpdateResult, ZoneType,
},
ServerFuture,
};
const TCP_TIMEOUT: u64 = 5;
pub fn start_dns_server(port: u16, domain: &str, runtime: &Runtime) -> color_eyre::Result<()> {
let dns_address = format!("127.0.0.1:{}", port);
eprintln!("Starting DNS server on {}", dns_address);
let mut catalog = Catalog::new();
let name = Name::from_str(domain).unwrap();
let authority = LocalhostAuthority {
name: name.clone().into(),
};
catalog.upsert(name.into(), Box::new(authority));
let mut server = ServerFuture::new(catalog);
let address: SocketAddr = dns_address.parse().unwrap();
let udp_socket = UdpSocket::bind(&address)?;
server.register_socket_std(udp_socket, runtime);
let tcp_listener = TcpListener::bind(&address)?;
server.register_listener_std(tcp_listener, Duration::from_secs(TCP_TIMEOUT), runtime)?;
Ok(())
}
struct LocalhostAuthority {
name: LowerName,
}
impl Authority for LocalhostAuthority {
type Lookup = AuthLookup;
type LookupFuture = future::Ready<Result<Self::Lookup, LookupError>>;
fn zone_type(&self) -> ZoneType {
ZoneType::Forward
}
fn is_axfr_allowed(&self) -> bool {
false
}
fn update(&mut self, _update: &MessageRequest) -> UpdateResult<bool> {
Err(ResponseCode::NotImp)
}
fn origin(&self) -> &LowerName {
&self.name
}
fn lookup(
&self,
name: &LowerName,
query_type: RecordType,
is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
let result: LookupResult<LookupRecords> = match query_type {
RecordType::A => {
let record =
Record::from_rdata(name.into(), 0, RData::A("127.0.0.1".parse().unwrap()));
Ok(LookupRecords::new(
is_secure,
Default::default(),
Arc::new(record.into()),
))
}
RecordType::AAAA => {
let record =
Record::from_rdata(name.into(), 0, RData::AAAA("::1".parse().unwrap()));
Ok(LookupRecords::new(
is_secure,
Default::default(),
Arc::new(record.into()),
))
}
_ => Err(LookupError::ResponseCode(ResponseCode::NXDomain)),
};
Box::pin(future::ready(
result.map(|answers| AuthLookup::answers(answers, None)),
))
}
fn search(
&self,
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
Box::pin(self.lookup(
query.name(),
query.query_type(),
is_secure,
supported_algorithms,
))
}
fn get_nsec_records(
&self,
_name: &LowerName,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> Pin<Box<dyn Future<Output = Result<Self::Lookup, LookupError>> + Send>> {
Box::pin(future::ok(AuthLookup::default()))
}
fn add_update_auth_key(&mut self, _name: Name, _key: KEY) -> DnsSecResult<()> {
Err("DNSSEC not supported".into())
}
fn add_zone_signing_key(&mut self, _signer: Signer) -> DnsSecResult<()> {
Err("DNSSEC not supported".into())
}
fn secure_zone(&mut self) -> DnsSecResult<()> {
Err("DNSSEC not supported".into())
}
}