scion_stack/resolver.rs
1// Copyright 2026 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! DNS resolution helpers for SCION addresses.
15
16pub mod txt;
17
18use async_trait::async_trait;
19use sciparse::address::ip_addr::ScionIpAddr;
20use thiserror::Error;
21
22/// DNS resolver trait for SCION address discovery.
23///
24/// Implementations return zero or more `ScionAddr` values for a given domain
25/// name. The resolver is expected to be async and safe to share across tasks.
26///
27/// # Error handling
28///
29/// Implementations SHOULD return `ResolveError::NoValidEntries` when a lookup
30/// completes but yields no valid SCION TXT entries, which includes a name that
31/// has no TXT records at all and a name that does not exist. Lookups that never
32/// complete (timeout, network error, server failure) SHOULD return
33/// `ResolveError::DnsLookup`, the only outcome a retry can change; callers
34/// branch on that with [`ResolveError::is_transient`] rather than on the
35/// variants. Partial failures SHOULD return the valid addresses and log
36/// warnings for invalid entries.
37#[async_trait]
38pub trait ScionDnsResolver: Send + Sync {
39 /// Resolve a domain into SCION addresses.
40 ///
41 /// Implementations SHOULD return only valid addresses and log warnings for
42 /// invalid TXT entries. Errors are reserved for lookup failures or when no
43 /// valid addresses can be produced.
44 async fn resolve(&self, domain: &str) -> Result<Vec<ScionIpAddr>, ResolveError>;
45}
46
47/// Errors returned by SCION DNS resolution.
48// Derives `PartialEq` for testability; as a consequence, `DnsLookup` carries a formatted message
49// rather than the underlying (non-`PartialEq`) DNS error. See API_CONVENTIONS.md.
50#[derive(Debug, Error, PartialEq)]
51#[non_exhaustive]
52pub enum ResolveError {
53 /// The lookup itself failed, for example on a timeout, a network error, or
54 /// a server failure. A retry may succeed.
55 #[error("dns lookup failed: {0}")]
56 DnsLookup(String),
57 /// The lookup completed without producing a usable SCION address: the TXT
58 /// records that exist do not parse as TSAR entries, the name has no TXT
59 /// records, or the name does not exist. A retry does not help for as long
60 /// as the answer stays valid.
61 #[error("no valid TSAR TXT entries for {domain}")]
62 NoValidEntries {
63 /// Domain name that was looked up.
64 domain: String,
65 /// Invalid entries encountered during parsing or TXT decoding. Empty
66 /// when the lookup returned no TXT records to parse.
67 invalid_entries: Vec<InvalidEntry>,
68 },
69}
70
71impl ResolveError {
72 /// Returns whether the failure is transient, so that a retry may help.
73 ///
74 /// Prefer this over matching the variants: the enum is `#[non_exhaustive]`,
75 /// and a new variant would silently fall into a caller's wildcard arm.
76 #[must_use]
77 pub fn is_transient(&self) -> bool {
78 match self {
79 Self::DnsLookup(_) => true,
80 Self::NoValidEntries { .. } => false,
81 }
82 }
83}
84
85/// Metadata for a TXT entry that could not be parsed.
86#[derive(Debug, Clone, PartialEq)]
87pub struct InvalidEntry {
88 raw: String,
89 reason: String,
90}
91
92impl InvalidEntry {
93 pub(crate) fn new(raw: impl Into<String>, reason: impl Into<String>) -> Self {
94 Self {
95 raw: raw.into(),
96 reason: reason.into(),
97 }
98 }
99
100 /// Return the raw TXT entry that failed parsing.
101 pub fn raw(&self) -> &str {
102 &self.raw
103 }
104
105 /// Return the reason this TXT entry failed parsing.
106 pub fn reason(&self) -> &str {
107 &self.reason
108 }
109}