#![forbid(unsafe_code)]
#![warn(
missing_docs,
rust_2018_idioms,
missing_debug_implementations,
rustdoc::broken_intra_doc_links
)]
use std::{
io, marker,
net::{SocketAddr, UdpSocket},
};
pub use dominion_parser::body::name::*;
pub use dominion_parser::body::*;
pub use dominion_parser::header::*;
pub use dominion_parser::*;
pub trait ServerService {
fn run<'a>(&self, client: SocketAddr, question: &'a DnsPacket<'a>) -> Option<DnsPacket<'a>>;
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct Builder;
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct Runner;
#[derive(Debug)]
pub struct Server<S> {
threads: usize,
socket: Option<UdpSocket>,
typestate: marker::PhantomData<S>,
}
impl<S> Server<S> {
pub fn new() -> Server<Builder> {
Server::<Builder>::default()
}
}
impl Default for Server<Builder> {
fn default() -> Self {
Server {
threads: 1,
socket: None,
typestate: marker::PhantomData,
}
}
}
impl Server<Builder> {
pub fn threads(mut self, n: usize) -> Self {
self.threads = n;
self
}
pub fn bind(self, addr: SocketAddr) -> Result<Server<Runner>, io::Error> {
Ok(Server {
threads: self.threads,
socket: Some(UdpSocket::bind(addr)?),
typestate: marker::PhantomData::<Runner>,
})
}
}
impl Server<Runner> {
pub fn serve<T>(self, srv: T)
where
T: ServerService + Sync,
{
std::thread::scope(|s| {
for _ in 0..self.threads {
s.spawn(|| {
self.serve_sth(&srv)
.expect("Unexpected error when sending or recieving from the socket")
});
}
})
}
fn serve_sth(&self, srv: &impl ServerService) -> Result<(), std::io::Error> {
let mut buff = [0; 512];
loop {
let (n, src) = self
.socket
.as_ref()
.expect("Runners can only be created with a active socket")
.recv_from(&mut buff)?;
let packet = match DnsPacket::try_from(&buff[..n]) {
Ok(packet) => packet,
Err(_) => continue,
};
if let Some(res) = srv.run(src, &packet) {
let serialized = Vec::<u8>::from(&res);
self.socket
.as_ref()
.expect("Runners can only be created with a active socket")
.send_to(&serialized[..], src)?;
};
}
}
}