1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! dns-server
//! ========
//! [](https://crates.io/crates/dns-server)
//! [](https://gitlab.com/leonhard-llc/ops/-/raw/main/dns-server/LICENSE)
//! [](https://github.com/rust-secure-code/safety-dance/)
//! [](https://gitlab.com/leonhard-llc/ops/-/pipelines)
//!
//! A threaded DNS server library.
//!
//! # Use Cases
//! - Make your API server its own DNS server.
//! This eliminates the DNS server as a separate point of failure.
//! - Keep your DNS config in code, next to your server code.
//! Include it in code reviews and integration tests.
//! - DNS-based
//! [domain validation for free ACME certificates](https://letsencrypt.org/how-it-works/).
//! This is useful for servers that don't listen on port 80.
//! Servers on port 80 can use HTTP for domain validation and don't need to use this.
//!
//! # Features
//! - Depends only on `std`
//! - `forbid(unsafe_code)`
//! - ?% test coverage
//!
//! # Limitations
//! - Brand new.
//!
//! # Example
//! ```
//! use dns_server::DnsRecord;
//! use permit::Permit;
//! use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
//! use signal_hook::iterator::Signals;
//!
//! let top_permit = Permit::new();
//! let permit = top_permit.new_sub();
//! # top_permit.revoke();
//! std::thread::spawn(move || {
//! Signals::new([SIGHUP, SIGINT, SIGQUIT, SIGTERM])
//! .unwrap()
//! .forever()
//! .next();
//! drop(top_permit);
//! });
//! let records = vec![
//! DnsRecord::new_a("aaa.example.com", "10.0.0.1").unwrap(),
//! DnsRecord::new_aaaa("aaa.example.com", "2606:2800:220:1:248:1893:25c8:1946").unwrap(),
//! DnsRecord::new_cname("bbb.example.com", "ccc.example.com").unwrap(),
//! ];
//! dns_server::Builder::new_port(8053)
//! .unwrap()
//! .with_permit(permit)
//! .serve_static(&records)
//! .unwrap();
//! ```
//!
//! # Related Crates
//!
//! # Cargo Geiger Safety Report
//! # Changelog
//! - v0.2.1
//! - New API supporting dynamic responses.
//! - Randomize order of static responses.
//! - v0.1.0 - Initial version
//!
//! # To Do
//! - Message compression
//! - Decide whether to send back error responses.
//! - Ergonomic constructors that take `OsStr`, for using environment variables
//! - Custom TTLs
//! - NS records (and glue)
//! - Client
//! - Caching client
//! - Recursive resolver
//!
//! # Alternatives
//!
#![forbid(unsafe_code)]
mod dns_class;
mod dns_message;
mod dns_message_header;
mod dns_name;
mod dns_op_code;
mod dns_question;
mod dns_record;
mod dns_response_code;
mod dns_type;
mod server;
pub use dns_class::DnsClass;
pub use dns_message::DnsMessage;
pub use dns_message_header::DnsMessageHeader;
pub use dns_name::DnsName;
pub use dns_op_code::DnsOpCode;
pub use dns_question::DnsQuestion;
pub use dns_record::DnsRecord;
pub use dns_response_code::DnsResponseCode;
pub use dns_type::DnsType;
pub use server::{process_datagram, process_request, serve_udp, Builder};
use fixed_buffer::FixedBuf;
fn read_exact<const N: usize, const M: usize>(buf: &mut FixedBuf<N>) -> Result<[u8; M], DnsError> {
let mut result = [0_u8; M];
buf.try_read_exact(&mut result).ok_or(DnsError::Truncated)?;
Ok(result)
}
fn read_u8<const N: usize>(buf: &mut FixedBuf<N>) -> Result<u8, DnsError> {
buf.try_read_byte().ok_or(DnsError::Truncated)
}
// fn write_u8<const N: usize>(out: &mut FixedBuf<N>, value: u8) -> Result<(), DnsError> {
// out.write_bytes(&[value])
// .map_err(|_| DnsError::ResponseBufferFull)?;
// Ok(())
// }
fn read_u16_be<const N: usize>(buf: &mut FixedBuf<N>) -> Result<u16, DnsError> {
let bytes: [u8; 2] = read_exact(buf)?;
Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
}
fn read_u32_be<const N: usize>(buf: &mut FixedBuf<N>) -> Result<u32, DnsError> {
let bytes: [u8; 4] = read_exact(buf)?;
Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
fn write_bytes<const N: usize>(out: &mut FixedBuf<N>, bytes: &[u8]) -> Result<(), DnsError> {
out.write_bytes(bytes)
.map_err(|_| DnsError::ResponseBufferFull)?;
Ok(())
}
fn write_u16_be<const N: usize>(out: &mut FixedBuf<N>, value: u16) -> Result<(), DnsError> {
let bytes: [u8; 2] = value.to_be_bytes();
out.write_bytes(&bytes)
.map_err(|_| DnsError::ResponseBufferFull)?;
Ok(())
}
fn write_u32_be<const N: usize>(out: &mut FixedBuf<N>, value: u32) -> Result<(), DnsError> {
let bytes: [u8; 4] = value.to_be_bytes();
out.write_bytes(&bytes)
.map_err(|_| DnsError::ResponseBufferFull)?;
Ok(())
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum DnsError {
InvalidClass,
InvalidLabel,
InvalidOpCode,
NameTooLong,
NoQuestion,
NotARequest,
NotFound,
ResponseBufferFull,
QueryHasAdditionalRecords,
QueryHasAnswer,
QueryHasNameServer,
TooManyAdditional,
TooManyAnswers,
TooManyLabels,
TooManyNameServers,
TooManyQuestions,
Truncated,
Internal(String),
Unreachable(&'static str, u32),
}