use ans_types::{BadgeStatus, CryptoError, ParseError};
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AnsError {
#[error("DNS error: {0}")]
Dns(#[from] DnsError),
#[error("Transparency log error: {0}")]
TransparencyLog(#[from] TlogError),
#[error("Certificate error: {0}")]
Certificate(#[from] CryptoError),
#[error("Verification error: {0}")]
Verification(#[from] VerificationError),
#[error("Parse error: {0}")]
Parse(#[from] ParseError),
}
pub type AnsResult<T> = Result<T, AnsError>;
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum DnsError {
#[error("DNS record not found (NXDOMAIN) for {fqdn}")]
NotFound {
fqdn: String,
},
#[error("DNS lookup failed for {fqdn}: {reason}")]
LookupFailed {
fqdn: String,
reason: String,
},
#[error("DNS query timed out for {fqdn}")]
Timeout {
fqdn: String,
},
#[error("DNSSEC validation failed for {fqdn}")]
DnssecFailed {
fqdn: String,
},
#[error("Invalid badge TXT record format: {record}")]
InvalidFormat {
record: String,
},
#[error("DNS resolver error: {0}")]
ResolverError(String),
}
#[derive(Debug)]
pub struct HttpError {
inner: reqwest::Error,
}
impl std::fmt::Display for HttpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::error::Error for HttpError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.source()
}
}
impl From<reqwest::Error> for HttpError {
fn from(err: reqwest::Error) -> Self {
Self { inner: err }
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum TlogError {
#[error("HTTP request failed: {0}")]
HttpError(#[from] HttpError),
#[error("Badge not found at {url}")]
NotFound {
url: String,
},
#[error("Invalid badge response: {0}")]
InvalidResponse(String),
#[error("Transparency log service unavailable")]
ServiceUnavailable,
#[error("Invalid URL: {0}")]
InvalidUrl(String),
#[error("Invalid header: {0}")]
InvalidHeader(String),
#[error("Untrusted badge domain: {domain}")]
UntrustedDomain {
domain: String,
trusted: Vec<String>,
},
}
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum VerificationError {
#[error("Badge status {status:?} is not valid for connections")]
InvalidStatus {
status: BadgeStatus,
},
#[error("Fingerprint mismatch: expected {expected}, got {actual}")]
FingerprintMismatch {
expected: String,
actual: String,
},
#[error("Hostname mismatch: expected {expected}, got {actual}")]
HostnameMismatch {
expected: String,
actual: String,
},
#[error("ANS name mismatch: expected {expected}, got {actual}")]
AnsNameMismatch {
expected: String,
actual: String,
},
#[error("No matching badge found for version {version}")]
NoMatchingBadge {
version: String,
},
#[error("Certificate does not chain to trusted CA")]
UntrustedCertificate,
#[error("DANE verification failed: {0}")]
DaneVerificationFailed(DaneError),
#[error("Multiple verification errors: {errors:?}")]
Multiple {
errors: Vec<Self>,
},
#[error("Configuration error: {0}")]
Configuration(String),
}
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum DaneError {
#[error("No TLSA records found for {fqdn}:{port}")]
NoTlsaRecords {
fqdn: String,
port: u16,
},
#[error("TLSA fingerprint mismatch: certificate not bound to DNS")]
FingerprintMismatch,
#[error("DNSSEC validation failed for {fqdn}")]
DnssecValidationFailed {
fqdn: String,
},
#[error("DNSSEC required but not present for {fqdn}")]
DnssecNotPresent {
fqdn: String,
},
#[error("Invalid TLSA record: {reason}")]
InvalidRecord {
reason: String,
},
#[error("DNS error during TLSA lookup: {0}")]
DnsError(#[from] DnsError),
}