cdns-rs 2.0.0-next.0

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2021  Aleksandr Morozov
 * Copyright (C) 2025  Aleksandr Morozov
 * Copyright (C) 2026  Aleksandr Morozov, 4neko.org
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::{error::Error, fmt::{self}};


pub(crate)
fn map_read_err(e: std::io::Error) -> CDnsError
{
    return CDnsError::new(CDnsErrorType::IoError, format!("{}", e));
}

 #[derive(Debug)]
pub struct ResolverStdErr;

impl ErrorReport for ResolverStdErr
{
    fn report(&self, error: crate::CDnsError) 
    {
        eprintln!("ERROR: {}", error);
    }
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CDnsIOError(CDnsError);

impl Error for CDnsIOError
{
    fn source(&self) -> Option<&(dyn Error + 'static)> 
    {
        Some(&self.0)
    }
}

impl fmt::Display for CDnsIOError
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
    {
        write!(f, "{}", self.source().unwrap())
    }
}

impl From<CDnsError> for CDnsIOError
{
    fn from(value: CDnsError) -> Self 
    {
        return Self(value);
    }
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CDnsError
{
    pub err_code: CDnsErrorType,
    pub message: String,
}

impl CDnsError
{
    pub fn new(err_code: CDnsErrorType, msg: String) -> Self
    {
        return CDnsError{err_code: err_code, message: msg};
    }
}

impl Error for CDnsError {}

impl fmt::Display for CDnsError 
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
    {
        write!(f, "cdns: [{}], {}", self.err_code, self.message)
    }
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CDnsErrorDnsResponse
{
    DnsUnknownReqID,
    DnsInvalidStatusBits,
    DnsResponseFromUnknownDestination,
    DnsBadFormat,
    DnsIncorrectRdlen,
    DnsProtocolViolation, 
    DnsUnknownRecordType,
    DnsUnknownQclassType,
    DnsRequestAnswerMismatch,
    DnsRecordAssertion,
    DnsPunycodeError,
    DnsHttpsParse,
}

impl fmt::Display for CDnsErrorDnsResponse 
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
    {
        match *self 
        {
            Self::DnsUnknownReqID => 
                write!(f, "Unknown Request ID"),
            Self::DnsInvalidStatusBits => 
                write!(f, "invalid bits in status field"),
            Self::DnsResponseFromUnknownDestination => 
                write!(f, "received a response from host which is not req origin"),
            Self::DnsBadFormat => 
                write!(f, "bad packet formatting"),
            Self::DnsIncorrectRdlen => 
                write!(f, "incorrect rdlen value"),
            Self::DnsProtocolViolation => 
                write!(f, "sever protocol violation"),
            Self::DnsUnknownRecordType =>
                write!(f, "unknown request recoed type"),
            Self::DnsUnknownQclassType => 
                write!(f, "unknwon qclass type"),
            Self::DnsRequestAnswerMismatch => 
                write!(f, "requst and anser are different"),
            Self::DnsRecordAssertion => 
                write!(f, "record parsing error"),
            Self::DnsPunycodeError => 
                write!(f, "punycode error"),
            Self::DnsHttpsParse => 
                write!(f, "http parsing error"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CDnsErrorType
{
    /// Error related to DNS response parsing
    DnsResponse(CDnsErrorDnsResponse),
    /// Received response with unknown ID in header
    RespIdMismatch,
    /// Internal error (assertions...)
    InternalError,
    /// A soft assertion which does not crash the whole program, but requires a fix
    SoftAssertion,
    /// Socket, File error
    IoError,
    /// Response was truncated
    MessageTruncated,
    /// Timeout event
    RequestTimeout,
    /// Error in configuraion file (format)
    ConfigError,
    /// Nameservers unreachable
    DnsNotAvailable,
    /// HTTP Errors
    HttpError,
    /// Connection type not supported
    SocketNotSupported,
    /// Received punycode is not ASCII
    PunycodeNotAscii,
}

impl fmt::Display for CDnsErrorType 
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
    {
        match self 
        {
            Self::DnsResponse(e) => 
                write!(f, "{}", e),
            Self::RespIdMismatch => 
                write!(f, "Response ID mismatch"),
            Self::InternalError => 
                write!(f, "Internal Error"),
            Self::SoftAssertion => 
                write!(f, "SoftAssertionTrap"),
            Self::IoError => 
                write!(f, "IO Error"),
            Self::MessageTruncated => 
                write!(f, "Message was truncated"),
            Self::RequestTimeout => 
                write!(f, "Request receive timout"),
            Self::ConfigError => 
                write!(f, "Config file error"),
            Self::DnsNotAvailable => 
                write!(f, "DNS not available"),
            Self::HttpError => 
                write!(f, "HTTP Error"),
            Self::SocketNotSupported => 
                write!(f, "Socket connection is not supported"),
            Self::PunycodeNotAscii => 
                write!(f, "Received punycode with non ASCII characters"),
        }
    }
}

pub type CDnsResult<T> = Result<T, CDnsError>;

#[macro_export]
macro_rules! internal_error 
{
    ($src:expr,$($arg:tt)*) => (
        return std::result::Result::Err($crate::CDnsError::new($src, format!($($arg)*)))
    )
}

#[macro_export]
macro_rules! internal_error_map
{
    ($src:expr,$($arg:tt)*) => (
        $crate::CDnsError::new($src, format!($($arg)*))
    )
}

#[cfg(feature = "no_error_output")]
#[macro_export]
macro_rules! write_error 
{
    ($src:expr) => (
        {let _ = $src;}
    )
}

#[cfg(not(feature = "no_error_output"))]
#[macro_export]
macro_rules! write_error 
{
    ($src:expr) => (
        eprintln!("{}", $src)
    )
}

pub trait ErrorReport: fmt::Debug
{
    fn report(&self, error: CDnsError);
}