Skip to main content

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` only when a
30/// lookup succeeds but yields no valid SCION TXT entries. Partial failures
31/// SHOULD return the valid addresses and log warnings for invalid entries.
32#[async_trait]
33pub trait ScionDnsResolver: Send + Sync {
34    /// Resolve a domain into SCION addresses.
35    ///
36    /// Implementations SHOULD return only valid addresses and log warnings for
37    /// invalid TXT entries. Errors are reserved for lookup failures or when no
38    /// valid addresses can be produced.
39    async fn resolve(&self, domain: &str) -> Result<Vec<ScionIpAddr>, ResolveError>;
40}
41
42/// Errors returned by SCION DNS resolution.
43// Derives `PartialEq` for testability; as a consequence, `DnsLookup` carries a formatted message
44// rather than the underlying (non-`PartialEq`) DNS error. See API_CONVENTIONS.md.
45#[derive(Debug, Error, PartialEq)]
46#[non_exhaustive]
47pub enum ResolveError {
48    /// DNS lookup failed.
49    #[error("dns lookup failed: {0}")]
50    DnsLookup(String),
51    /// No valid TSAR entries were found.
52    #[error("no valid TSAR TXT entries for {domain}")]
53    NoValidEntries {
54        /// Domain name that was looked up.
55        domain: String,
56        /// Invalid entries encountered during parsing or TXT decoding.
57        invalid_entries: Vec<InvalidEntry>,
58    },
59}
60
61/// Metadata for a TXT entry that could not be parsed.
62#[derive(Debug, Clone, PartialEq)]
63pub struct InvalidEntry {
64    raw: String,
65    reason: String,
66}
67
68impl InvalidEntry {
69    pub(crate) fn new(raw: impl Into<String>, reason: impl Into<String>) -> Self {
70        Self {
71            raw: raw.into(),
72            reason: reason.into(),
73        }
74    }
75
76    /// Return the raw TXT entry that failed parsing.
77    pub fn raw(&self) -> &str {
78        &self.raw
79    }
80
81    /// Return the reason this TXT entry failed parsing.
82    pub fn reason(&self) -> &str {
83        &self.reason
84    }
85}