1#![cfg(feature = "recursor")]
9
10use std::{io, path::Path, time::Instant};
13
14#[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
15use crate::resolver::{OpportunisticEncryptionStatePersistTask, config::OpportunisticEncryption};
16#[cfg(feature = "__dnssec")]
17use crate::{dnssec::NxProofKind, zone_handler::Nsec3QueryInfo};
18use crate::{
19 net::runtime::RuntimeProvider,
20 proto::{
21 op::Query,
22 rr::{LowerName, Name, RecordType},
23 },
24 resolver::recursor::{RecursiveConfig, Recursor},
25 server::{Request, RequestInfo},
26 zone_handler::{
27 AuthLookup, AxfrPolicy, LookupControlFlow, LookupError, LookupOptions, ZoneHandler,
28 ZoneType,
29 },
30};
31use hickory_proto::rr::TSigResponseContext;
32use tracing::{debug, info};
33
34pub struct RecursiveZoneHandler<P: RuntimeProvider> {
38 origin: LowerName,
39 recursor: Recursor<P>,
40 #[allow(dead_code)] opportunistic_encryption_persistence_task: Option<P::Handle>,
42}
43
44impl<P: RuntimeProvider> RecursiveZoneHandler<P> {
45 pub async fn try_from_config(
47 origin: Name,
48 _zone_type: ZoneType,
49 config: RecursiveConfig,
50 root_dir: Option<&Path>,
51 conn_provider: P,
52 ) -> Result<Self, String> {
53 info!("loading recursor config: {}", origin);
54
55 #[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
56 let persistence_config = match &config.options.opportunistic_encryption {
57 OpportunisticEncryption::Enabled { config } => config.persistence.clone(),
58 _ => None,
59 };
60
61 let recursor = Recursor::from_config(config, root_dir, conn_provider.clone())
62 .map_err(|e| format!("failed to build recursor: {e}"))?;
63
64 Ok(Self {
65 origin: origin.into(),
66 #[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
69 opportunistic_encryption_persistence_task: match persistence_config {
70 Some(config) => {
71 OpportunisticEncryptionStatePersistTask::<P::Timer>::start(
72 config,
73 recursor.pool_context(),
74 conn_provider.clone(),
75 )
76 .await?
77 }
78 _ => None,
79 },
80 #[cfg(not(all(feature = "toml", any(feature = "__tls", feature = "__quic"))))]
81 opportunistic_encryption_persistence_task: None,
82 recursor,
83 })
84 }
85}
86
87#[async_trait::async_trait]
88impl<P: RuntimeProvider> ZoneHandler for RecursiveZoneHandler<P> {
89 fn zone_type(&self) -> ZoneType {
91 ZoneType::External
92 }
93
94 fn axfr_policy(&self) -> AxfrPolicy {
96 AxfrPolicy::Deny
97 }
98
99 fn can_validate_dnssec(&self) -> bool {
100 self.recursor.is_validating()
101 }
102
103 fn origin(&self) -> &LowerName {
109 &self.origin
110 }
111
112 async fn lookup(
114 &self,
115 name: &LowerName,
116 rtype: RecordType,
117 _request_info: Option<&RequestInfo<'_>>,
118 lookup_options: LookupOptions,
119 ) -> LookupControlFlow<AuthLookup> {
120 debug!("recursive lookup: {} {}", name, rtype);
121
122 let query = Query::query(name.into(), rtype);
123 let now = Instant::now();
124
125 let result = self
126 .recursor
127 .resolve(query.clone(), now, lookup_options.dnssec_ok)
128 .await;
129
130 let response = match result {
131 Ok(response) => response,
132 Err(error) => return LookupControlFlow::Continue(Err(LookupError::from(error))),
133 };
134 LookupControlFlow::Continue(Ok(AuthLookup::Response(response)))
135 }
136
137 async fn search(
138 &self,
139 request: &Request,
140 lookup_options: LookupOptions,
141 ) -> (LookupControlFlow<AuthLookup>, Option<TSigResponseContext>) {
142 let request_info = match request.request_info() {
143 Ok(info) => info,
144 Err(e) => return (LookupControlFlow::Break(Err(e)), None),
145 };
146 (
147 self.lookup(
148 request_info.query.name(),
149 request_info.query.query_type(),
150 Some(&request_info),
151 lookup_options,
152 )
153 .await,
154 None,
155 )
156 }
157
158 async fn nsec_records(
159 &self,
160 _name: &LowerName,
161 _lookup_options: LookupOptions,
162 ) -> LookupControlFlow<AuthLookup> {
163 LookupControlFlow::Continue(Err(LookupError::from(io::Error::other(
164 "Getting NSEC records is unimplemented for the recursor",
165 ))))
166 }
167
168 #[cfg(feature = "__dnssec")]
169 async fn nsec3_records(
170 &self,
171 _info: Nsec3QueryInfo<'_>,
172 _lookup_options: LookupOptions,
173 ) -> LookupControlFlow<AuthLookup> {
174 LookupControlFlow::Continue(Err(LookupError::from(io::Error::other(
175 "getting NSEC3 records is unimplemented for the recursor",
176 ))))
177 }
178
179 #[cfg(feature = "__dnssec")]
180 fn nx_proof_kind(&self) -> Option<&NxProofKind> {
181 None
182 }
183
184 #[cfg(feature = "metrics")]
185 fn metrics_label(&self) -> &'static str {
186 "recursive"
187 }
188}