1#![cfg(feature = "resolver")]
9
10use std::io;
13#[cfg(feature = "__dnssec")]
14use std::sync::Arc;
15
16use serde::Deserialize;
17use tracing::{debug, info};
18
19#[cfg(feature = "metrics")]
20use crate::store::metrics::QueryStoreMetrics;
21#[cfg(feature = "__dnssec")]
22use crate::{authority::Nsec3QueryInfo, dnssec::NxProofKind, proto::dnssec::TrustAnchors};
23use crate::{
24 authority::{
25 Authority, LookupControlFlow, LookupError, LookupObject, LookupOptions, MessageRequest,
26 UpdateResult, ZoneType,
27 },
28 proto::{
29 op::ResponseCode,
30 rr::{LowerName, Name, Record, RecordType},
31 },
32 resolver::{
33 Resolver,
34 config::{NameServerConfigGroup, ResolveHosts, ResolverConfig, ResolverOpts},
35 lookup::Lookup as ResolverLookup,
36 name_server::{ConnectionProvider, TokioConnectionProvider},
37 },
38 server::RequestInfo,
39};
40
41pub struct ForwardAuthorityBuilder<P: ConnectionProvider> {
45 origin: Name,
46 config: ForwardConfig,
47 domain: Option<Name>,
48 search: Vec<Name>,
49 runtime: P,
50
51 #[cfg(feature = "__dnssec")]
52 trust_anchor: Option<Arc<TrustAnchors>>,
53}
54
55impl<P: ConnectionProvider> ForwardAuthorityBuilder<P> {
56 pub fn with_origin(mut self, origin: Name) -> Self {
58 self.origin = origin;
59 self
60 }
61
62 #[cfg(feature = "__dnssec")]
67 pub fn with_trust_anchor(mut self, trust_anchor: Arc<TrustAnchors>) -> Self {
68 self.trust_anchor = Some(trust_anchor);
69 self
70 }
71
72 pub fn options_mut(&mut self) -> &mut ResolverOpts {
74 self.config
75 .options
76 .get_or_insert_with(ResolverOpts::default)
77 }
78
79 pub fn with_domain(mut self, domain: Name) -> Self {
81 self.domain = Some(domain);
82 self
83 }
84
85 pub fn with_search(mut self, search: Vec<Name>) -> Self {
87 self.search = search;
88 self
89 }
90
91 pub fn build(self) -> Result<ForwardAuthority<P>, String> {
93 let Self {
94 origin,
95 config,
96 domain,
97 search,
98 runtime,
99 #[cfg(feature = "__dnssec")]
100 trust_anchor,
101 } = self;
102 info!(%origin, "loading forwarder config");
103
104 let name_servers = config.name_servers;
105 let mut options = config.options.unwrap_or_default();
106
107 if !options.preserve_intermediates {
119 tracing::warn!(
120 "preserve_intermediates set to false, which is invalid \
121 for a forwarder; switching to true"
122 );
123 options.preserve_intermediates = true;
124 }
125
126 if options.use_hosts_file == ResolveHosts::Auto {
129 options.use_hosts_file = ResolveHosts::Never;
130 }
131
132 let config = ResolverConfig::from_parts(domain, search, name_servers);
133
134 let mut resolver_builder = Resolver::builder_with_config(config, runtime);
135
136 #[cfg(feature = "__dnssec")]
137 match (trust_anchor, &options.trust_anchor) {
138 (Some(trust_anchor), _) => {
139 resolver_builder = resolver_builder.with_trust_anchor(trust_anchor);
140 options.validate = true;
141 }
142 (None, Some(path)) => {
143 let trust_anchor = TrustAnchors::from_file(path).map_err(|err| err.to_string())?;
144 resolver_builder = resolver_builder.with_trust_anchor(Arc::new(trust_anchor));
145 options.validate = true;
146 }
147 (None, None) => {}
148 }
149
150 *resolver_builder.options_mut() = options;
151 let resolver = resolver_builder.build();
152
153 info!(%origin, "forward resolver configured");
154
155 Ok(ForwardAuthority {
156 origin: origin.into(),
157 resolver,
158 #[cfg(feature = "metrics")]
159 metrics: QueryStoreMetrics::new("forwarder"),
160 })
161 }
162}
163
164pub struct ForwardAuthority<P: ConnectionProvider = TokioConnectionProvider> {
168 origin: LowerName,
169 resolver: Resolver<P>,
170 #[cfg(feature = "metrics")]
171 metrics: QueryStoreMetrics,
172}
173
174impl<P: ConnectionProvider> ForwardAuthority<P> {
175 pub fn builder(runtime: P) -> Result<ForwardAuthorityBuilder<P>, String> {
178 let (resolver_config, options) = hickory_resolver::system_conf::read_system_conf()
179 .map_err(|e| format!("error reading system configuration: {e}"))?;
180 let forward_config = ForwardConfig {
181 name_servers: resolver_config.name_servers().to_vec().into(),
182 options: Some(options),
183 };
184 let mut builder = Self::builder_with_config(forward_config, runtime);
185 if let Some(domain) = resolver_config.domain() {
186 builder = builder.with_domain(domain.clone());
187 }
188 builder = builder.with_search(resolver_config.search().to_vec());
189 Ok(builder)
190 }
191
192 pub fn builder_with_config(config: ForwardConfig, runtime: P) -> ForwardAuthorityBuilder<P> {
194 ForwardAuthorityBuilder {
195 origin: Name::root(),
196 config,
197 domain: None,
198 search: vec![],
199 runtime,
200 #[cfg(feature = "__dnssec")]
201 trust_anchor: None,
202 }
203 }
204}
205
206impl ForwardAuthority<TokioConnectionProvider> {
207 pub fn builder_tokio(
209 config: ForwardConfig,
210 ) -> ForwardAuthorityBuilder<TokioConnectionProvider> {
211 Self::builder_with_config(config, TokioConnectionProvider::default())
212 }
213}
214
215#[async_trait::async_trait]
216impl<P: ConnectionProvider> Authority for ForwardAuthority<P> {
217 type Lookup = ForwardLookup;
218
219 fn zone_type(&self) -> ZoneType {
221 ZoneType::External
222 }
223
224 fn is_axfr_allowed(&self) -> bool {
226 false
227 }
228
229 fn can_validate_dnssec(&self) -> bool {
231 self.resolver.options().validate
232 }
233
234 async fn update(&self, _update: &MessageRequest) -> UpdateResult<bool> {
235 Err(ResponseCode::NotImp)
236 }
237
238 fn origin(&self) -> &LowerName {
244 &self.origin
245 }
246
247 async fn lookup(
249 &self,
250 name: &LowerName,
251 rtype: RecordType,
252 _lookup_options: LookupOptions,
253 ) -> LookupControlFlow<Self::Lookup> {
254 debug_assert!(self.origin.zone_of(name));
256
257 debug!("forwarding lookup: {} {}", name, rtype);
258
259 let mut name: Name = name.clone().into();
262 name.set_fqdn(false);
263
264 use LookupControlFlow::*;
265 let lookup = match self.resolver.lookup(name, rtype).await {
266 Ok(lookup) => Continue(Ok(ForwardLookup(lookup))),
267 Err(e) => Continue(Err(LookupError::from(e))),
268 };
269
270 #[cfg(feature = "metrics")]
271 self.metrics.increment_lookup(&lookup);
272
273 lookup
274 }
275
276 async fn search(
277 &self,
278 request_info: RequestInfo<'_>,
279 lookup_options: LookupOptions,
280 ) -> LookupControlFlow<Self::Lookup> {
281 self.lookup(
282 request_info.query.name(),
283 request_info.query.query_type(),
284 lookup_options,
285 )
286 .await
287 }
288
289 async fn get_nsec_records(
290 &self,
291 _name: &LowerName,
292 _lookup_options: LookupOptions,
293 ) -> LookupControlFlow<Self::Lookup> {
294 LookupControlFlow::Continue(Err(LookupError::from(io::Error::new(
295 io::ErrorKind::Other,
296 "Getting NSEC records is unimplemented for the forwarder",
297 ))))
298 }
299
300 #[cfg(feature = "__dnssec")]
301 async fn get_nsec3_records(
302 &self,
303 _info: Nsec3QueryInfo<'_>,
304 _lookup_options: LookupOptions,
305 ) -> LookupControlFlow<Self::Lookup> {
306 LookupControlFlow::Continue(Err(LookupError::from(io::Error::new(
307 io::ErrorKind::Other,
308 "getting NSEC3 records is unimplemented for the forwarder",
309 ))))
310 }
311
312 #[cfg(feature = "__dnssec")]
313 fn nx_proof_kind(&self) -> Option<&NxProofKind> {
314 None
315 }
316}
317
318pub struct ForwardLookup(pub ResolverLookup);
322
323impl LookupObject for ForwardLookup {
324 fn is_empty(&self) -> bool {
325 self.0.is_empty()
326 }
327
328 fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a> {
329 Box::new(self.0.record_iter())
330 }
331
332 fn take_additionals(&mut self) -> Option<Box<dyn LookupObject>> {
333 None
334 }
335}
336
337#[derive(Clone, Deserialize, Debug)]
339#[serde(deny_unknown_fields)]
340pub struct ForwardConfig {
341 pub name_servers: NameServerConfigGroup,
343 pub options: Option<ResolverOpts>,
345}