dnscrypt 0.1.0

A pure-Rust DNSCrypt v2 client library — sync and async support.
Documentation
//! Unified error type for `dnscrypto`.

use std::fmt;

/// Errors that can occur during a DNSCrypt session.
#[derive(Debug)]
pub enum Error {
    /// A network-level error occurred (UDP/TCP send, receive, or connect).
    Network(String),
    /// The DNSCrypt resolver certificate was missing, invalid, or expired.
    CertificateError(String),
    /// Poly1305 authentication of an encrypted DNS packet failed.
    ///
    /// This indicates tampered or corrupted data and should be treated as a
    /// security event.
    AuthenticationFailure,
    /// The decrypted DNS response contained no A record for the queried name.
    NoRecord,
    /// A protocol-level invariant was violated (e.g. wrong magic bytes,
    /// nonce mismatch, or malformed packet structure).
    Protocol(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Network(msg) => write!(f, "DNSCrypt network error: {msg}"),
            Error::CertificateError(msg) => write!(f, "DNSCrypt certificate error: {msg}"),
            Error::AuthenticationFailure => {
                write!(f, "DNSCrypt authentication failure (Poly1305 tag mismatch)")
            }
            Error::NoRecord => write!(f, "DNSCrypt: no A record found in response"),
            Error::Protocol(msg) => write!(f, "DNSCrypt protocol error: {msg}"),
        }
    }
}

impl std::error::Error for Error {}

/// Convert a bare `String` error (from internal helpers) into the most
/// appropriate [`Error`] variant.  Internal helpers still use `String` for
/// ergonomics; this conversion happens at the public API boundary.
impl From<String> for Error {
    fn from(s: String) -> Self {
        // Classify by message prefix emitted by sub-modules.
        if s.contains("authentication") || s.contains("Poly1305") {
            Error::AuthenticationFailure
        } else if s.contains("TCP") || s.contains("UDP") || s.contains("socket") {
            Error::Network(s)
        } else if s.contains("cert") || s.contains("Cert") || s.contains("serial") {
            Error::CertificateError(s)
        } else {
            Error::Protocol(s)
        }
    }
}