use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EtchDnsError {
#[error("Failed to parse configuration: {0}")]
ConfigParseError(String),
#[error("Failed to read configuration file: {0}")]
ConfigReadError(String),
#[error("Failed to bind to socket: {0}")]
SocketBindError(#[source] io::Error),
#[error("Failed to send data: {0}")]
#[allow(dead_code)]
SendError(#[source] io::Error),
#[error("Failed to receive data: {0}")]
#[allow(dead_code)]
ReceiveError(#[source] io::Error),
#[error("DNS packet processing error: {0}")]
DnsProcessingError(#[from] DnsError),
#[error("Client management error: {0}")]
#[allow(dead_code)]
ClientError(String),
#[error("Failed to drop privileges: {0}")]
PrivilegeDropError(String),
#[error("Other error: {0}")]
Other(String),
}
#[derive(Debug, Error)]
pub enum DnsError {
#[error("DNS packet parsing error: {0}")]
#[allow(dead_code)]
ParseError(String),
#[error("Invalid DNS packet: {0}")]
InvalidPacket(String),
#[error("Unsupported DNS operation: {0}")]
UnsupportedOperation(String),
#[error("DNS packet too short at offset {offset}")]
PacketTooShort { offset: usize },
#[error("DNS packet too large: {size} bytes (max {max_size} bytes)")]
PacketTooLarge { size: usize, max_size: usize },
#[error("Invalid domain name: {0}")]
InvalidDomainName(String),
#[error("Domain name label too long: {length} bytes (max 63 bytes)")]
LabelTooLong { length: usize },
#[error("Domain name too long: {length} bytes (max {max_length} bytes)")]
DomainNameTooLong { length: usize, max_length: usize },
#[error("Invalid character '{character}' at position {position} in label '{label}'")]
InvalidDomainNameCharacter {
character: char,
position: usize,
label: String,
},
#[error("Empty label in domain name")]
EmptyLabel,
#[error("Too many compression pointers: {count} (max 10)")]
TooManyCompressionPointers { count: usize },
#[error(
"Invalid compression pointer at offset {offset}: points to {pointer} (packet size: {packet_size})"
)]
InvalidCompressionPointer {
offset: usize,
pointer: usize,
packet_size: usize,
},
#[error("Invalid record: {0}")]
InvalidRecord(String),
#[error("Invalid question: {0}")]
InvalidQuestion(String),
#[error("Invalid EDNS data: {0}")]
InvalidEdns(String),
#[error("Invalid EDNS-client-subnet data: {0}")]
#[allow(dead_code)]
InvalidEdnsClientSubnet(String),
#[error("Upstream DNS server error: {0}")]
UpstreamError(String),
#[error("Upstream DNS server timeout")]
UpstreamTimeout,
#[error("Query is already in flight")]
AlreadyInFlight(tokio::sync::broadcast::Receiver<crate::query_manager::DnsResponse>),
#[error("Other DNS error: {0}")]
Other(String),
}
pub type DnsResult<T> = Result<T, DnsError>;
pub type EtchDnsResult<T> = Result<T, EtchDnsError>;
impl From<io::Error> for DnsError {
fn from(error: io::Error) -> Self {
match error.kind() {
io::ErrorKind::TimedOut => DnsError::UpstreamTimeout,
_ => DnsError::Other(error.to_string()),
}
}
}
impl From<String> for DnsError {
fn from(error: String) -> Self {
DnsError::Other(error)
}
}
impl From<&str> for DnsError {
fn from(error: &str) -> Self {
DnsError::Other(error.to_string())
}
}
impl DnsError {
pub fn into_receiver(
self,
) -> Option<tokio::sync::broadcast::Receiver<crate::query_manager::DnsResponse>> {
match self {
DnsError::AlreadyInFlight(receiver) => Some(receiver),
_ => None,
}
}
}
impl EtchDnsError {
#[allow(dead_code)]
pub fn into_receiver(
self,
) -> Option<tokio::sync::broadcast::Receiver<crate::query_manager::DnsResponse>> {
match self {
EtchDnsError::DnsProcessingError(dns_error) => dns_error.into_receiver(),
_ => None,
}
}
}