Skip to main content

netcore/
dns.rs

1//! DNS resolution results.
2//!
3//! The stub-vs-upstream distinction matters. `/etc/resolv.conf` on this
4//! machine says `127.0.0.53` but the real server answering queries is
5//! `fe80::dead:beef:feed:cafe%eth0`. Users debugging DNS failures want to
6//! see which server actually answered, not just "the stub."
7
8use std::net::IpAddr;
9use std::time::Duration;
10
11use serde::{Deserialize, Serialize};
12
13use crate::connection::Family;
14
15/// Result of a single DNS resolution attempt.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct DnsResolution {
18    /// The name that was queried.
19    pub queried: String,
20    /// Which resolver path was used.
21    pub via: DnsSource,
22    /// Upstream the stub forwarded to, if known (parsed from `resolvectl`).
23    pub upstream_used: Option<IpAddr>,
24    /// Answers returned for the query.
25    pub answers: Vec<DnsAnswer>,
26    /// How long the resolution took.
27    pub took: Duration,
28    /// True when the answer was served from the resolver's cache.
29    pub cached: bool,
30    /// True when the answer was DNSSEC-validated.
31    pub authenticated: bool,
32    /// Set when the resolver returned an error instead of answers.
33    pub error: Option<DnsError>,
34}
35
36/// Which DNS resolver path was used to answer the query.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(tag = "kind", content = "addr", rename_all = "snake_case")]
39pub enum DnsSource {
40    /// systemd-resolved or dnsmasq listening on loopback.
41    Stub(IpAddr),
42    /// A non-loopback resolver was queried directly.
43    Direct(IpAddr),
44    /// Multicast DNS / .local.
45    Mdns,
46    /// Libc `getaddrinfo`; the answering server is not known.
47    Libc,
48}
49
50/// A single DNS A or AAAA record returned by a resolver.
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct DnsAnswer {
53    /// The resolved IP address.
54    pub ip: IpAddr,
55    /// Address family of `ip`.
56    pub family: Family,
57    /// Time-to-live in seconds, when reported by the resolver.
58    pub ttl: Option<u32>,
59}
60
61/// A DNS resolution error.
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(tag = "kind", rename_all = "snake_case")]
64pub enum DnsError {
65    /// The name does not exist (`NXDOMAIN`).
66    NxDomain,
67    /// The server encountered an internal error (`SERVFAIL`).
68    ServFail,
69    /// The resolver did not respond within the deadline.
70    Timeout,
71    /// Name syntactically invalid or resolver refused.
72    Other(String),
73}