1use std::{collections::HashMap, iter, sync::Arc};
12
13use tracing::{debug, error, info, trace, warn};
14
15#[cfg(feature = "metrics")]
16use crate::metrics::CatalogMetrics;
17#[cfg(feature = "__dnssec")]
18use crate::net::{DnsError, NetError};
19#[cfg(all(feature = "__dnssec", feature = "recursor"))]
20use crate::resolver::recursor;
21#[cfg(feature = "__dnssec")]
22use crate::{
23 dnssec::NxProofKind,
24 proto::{
25 dnssec::{DnssecSummary, Proof, rdata::DNSSECRData},
26 rr::RData,
27 serialize::binary::BinEncoder,
28 },
29 zone_handler::Nsec3QueryInfo,
30};
31use crate::{
32 net::runtime::Time,
33 proto::{
34 op::{Edns, LowerQuery, Message, MessageType, Metadata, OpCode, ResponseCode},
35 rr::{
36 LowerName, Record, RecordType,
37 rdata::opt::{EdnsCode, EdnsOption, NSIDPayload},
38 },
39 },
40 server::{Request, RequestHandler, RequestInfo, ResponseHandler, ResponseInfo},
41 zone_handler::{
42 AuthLookup, LookupControlFlow, LookupError, LookupOptions, LookupRecords,
43 MessageResponseBuilder, ZoneHandler, ZoneType,
44 },
45};
46
47#[derive(Default)]
49pub struct Catalog {
50 nsid_payload: Option<NSIDPayload>,
51 handlers: HashMap<LowerName, Vec<Arc<dyn ZoneHandler>>>,
52 #[cfg(feature = "metrics")]
53 metrics: CatalogMetrics,
54}
55
56#[async_trait::async_trait]
57impl RequestHandler for Catalog {
58 async fn handle_request<R: ResponseHandler, T: Time>(
65 &self,
66 request: &Request,
67 response_handle: R,
68 ) -> ResponseInfo {
69 trace!("request: {:?}", request);
70
71 let mut resp_edns: Edns;
72
73 let response_edns = if let Some(req_edns) = request.edns.as_ref() {
75 resp_edns = Edns::new();
76
77 let our_version = 0;
80 #[cfg(feature = "__dnssec")]
81 resp_edns.set_dnssec_ok(req_edns.flags().dnssec_ok);
82 resp_edns.set_max_payload(req_edns.max_payload().max(512));
83 resp_edns.set_version(our_version);
84
85 if req_edns.version() > our_version {
86 warn!(
87 "request edns version greater than {}: {}",
88 our_version,
89 req_edns.version()
90 );
91 return send_error_response(
92 request,
93 ResponseCode::BADVERS,
94 Some(&resp_edns),
95 response_handle,
96 )
97 .await;
98 }
99
100 match (req_edns.option(EdnsCode::NSID), &self.nsid_payload) {
102 (Some(request_option), Some(payload)) => {
105 if !request_option.is_empty() {
108 warn!("ignoring non-empty EDNS NSID request payload")
109 }
110 resp_edns
111 .options_mut()
112 .insert(EdnsOption::NSID(payload.clone()));
113 }
114 (Some(_), None) => {
116 trace!("ignoring EDNS NSID request - no response payload configured")
117 }
118 (None, _) => {}
121 };
122
123 Some(&resp_edns)
124 } else {
125 None
126 };
127
128 let now = T::current_time();
129 match request.metadata.message_type {
130 MessageType::Query => match request.metadata.op_code {
133 OpCode::Query => {
134 debug!("query received: {}", request.metadata.id);
135 self.lookup(request, response_edns, now, response_handle)
136 .await
137 }
138 OpCode::Update => {
139 debug!("update received: {}", request.metadata.id);
140 self.update(request, response_edns, now, response_handle)
141 .await
142 }
143 c => {
144 warn!("unimplemented op_code: {:?}", c);
145 send_error_response(
146 request,
147 ResponseCode::NotImp,
148 response_edns,
149 response_handle,
150 )
151 .await
152 }
153 },
154 MessageType::Response => {
155 warn!(
156 "got a response as a request from id: {}",
157 request.metadata.id
158 );
159 send_error_response(
160 request,
161 ResponseCode::FormErr,
162 response_edns,
163 response_handle,
164 )
165 .await
166 }
167 }
168 }
169}
170
171impl Catalog {
172 pub fn new() -> Self {
174 Self {
175 handlers: HashMap::new(),
176 nsid_payload: None,
177 #[cfg(feature = "metrics")]
178 metrics: CatalogMetrics::default(),
179 }
180 }
181
182 pub fn upsert(&mut self, name: LowerName, handlers: Vec<Arc<dyn ZoneHandler>>) {
189 #[cfg(feature = "metrics")]
190 for handler in handlers.iter() {
191 self.metrics.add_handler(handler.as_ref())
192 }
193
194 self.handlers.insert(name, handlers);
195 }
196
197 pub fn remove(&mut self, name: &LowerName) -> Option<Vec<Arc<dyn ZoneHandler>>> {
199 self.handlers.remove(name)
202 }
203
204 pub fn set_nsid(&mut self, payload: Option<NSIDPayload>) {
211 self.nsid_payload = payload
212 }
213
214 pub fn nsid(&self) -> Option<&NSIDPayload> {
218 self.nsid_payload.as_ref()
219 }
220
221 pub async fn update<R: ResponseHandler>(
272 &self,
273 update: &Request,
274 response_edns: Option<&Edns>,
275 now: u64,
276 mut response_handle: R,
277 ) -> ResponseInfo {
278 let Ok(request_info) = update.request_info() else {
286 warn!("invalid update request, zone count must be one");
287 return send_error_response(
288 update,
289 ResponseCode::FormErr,
290 response_edns,
291 response_handle,
292 )
293 .await;
294 };
295 let ztype = request_info.query.query_type();
296
297 if ztype != RecordType::SOA {
298 warn!("invalid update request zone type must be SOA, ztype: {ztype}");
299 return send_error_response(
300 update,
301 ResponseCode::FormErr,
302 response_edns,
303 response_handle,
304 )
305 .await;
306 }
307
308 if let Some(handlers) = self.find(request_info.query.name()) {
310 #[allow(clippy::never_loop)]
311 for handler in handlers {
312 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
313 let (response_code, signer) = match handler.zone_type() {
314 ZoneType::Secondary => {
315 error!("secondary forwarding for update not yet implemented");
316 (ResponseCode::NotImp, None)
317 }
318 ZoneType::Primary => {
319 let (update_result, signer) = handler.update(update, now).await;
320 match update_result {
321 Ok(_) => (ResponseCode::NoError, signer),
323 Err(response_code) => (response_code, signer),
324 }
325 }
326 _ => (ResponseCode::NotAuth, None),
327 };
328
329 let response = MessageResponseBuilder::new(&update.queries, response_edns);
330 let mut response_meta =
331 Metadata::new(update.metadata.id, MessageType::Response, OpCode::Update);
332 response_meta.response_code = response_code;
333 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
334 let mut response = response.build_no_records(response_meta);
335
336 #[cfg(feature = "__dnssec")]
337 if let Some(signer) = signer {
338 let mut tbs_response_buf = Vec::with_capacity(512);
339 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
340 let mut response_meta =
341 Metadata::new(update.metadata.id, MessageType::Response, OpCode::Update);
342 response_meta.response_code = response_code;
343 let tbs_response = MessageResponseBuilder::new(&update.queries, response_edns)
344 .build_no_records(response_meta);
345 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
346 error!(%error, "error encoding response");
347 return send_error_response(
348 update,
349 ResponseCode::ServFail,
350 response_edns,
351 response_handle,
352 )
353 .await;
354 }
355 match signer.sign(&tbs_response_buf) {
356 Ok(signature) => response.set_signature(signature),
357 Err(error) => {
358 error!(%error, "error signing response");
359 return send_error_response(
360 update,
361 ResponseCode::ServFail,
362 response_edns,
363 response_handle,
364 )
365 .await;
366 }
367 }
368 }
369
370 match response_handle.send_response(response).await {
371 Err(error) => {
372 error!(%error, "error sending message");
373 return ResponseInfo::serve_failed(update);
374 }
375 Ok(response_info) => return response_info,
376 }
377 }
378 };
379
380 send_error_response(
381 update,
382 ResponseCode::ServFail,
383 response_edns,
384 response_handle,
385 )
386 .await
387 }
388
389 pub fn contains(&self, name: &LowerName) -> bool {
399 self.handlers.contains_key(name)
400 }
401
402 pub async fn lookup<R: ResponseHandler>(
410 &self,
411 request: &Request,
412 response_edns: Option<&Edns>,
413 now: u64,
414 response_handle: R,
415 ) -> ResponseInfo {
416 let Ok(request_info) = request.request_info() else {
417 return send_error_response(
419 request,
420 ResponseCode::FormErr,
421 response_edns,
422 response_handle,
423 )
424 .await;
425 };
426 let handlers = self.find(request_info.query.name());
427
428 let Some(handlers) = handlers else {
429 return send_error_response(
431 request,
432 ResponseCode::Refused,
433 response_edns,
434 response_handle,
435 )
436 .await;
437 };
438
439 if request_info.query.query_type() == RecordType::AXFR {
440 zone_transfer(
441 request_info,
442 handlers,
443 request,
444 response_edns,
445 now,
446 response_handle.clone(),
447 )
448 .await
449 } else {
450 lookup(
451 request_info,
452 handlers,
453 request,
454 response_edns,
455 response_handle.clone(),
456 #[cfg(feature = "metrics")]
457 &self.metrics,
458 )
459 .await
460 }
461 }
462
463 pub fn find(&self, name: &LowerName) -> Option<&Vec<Arc<dyn ZoneHandler + 'static>>> {
465 debug!("searching zone handlers for: {name}");
466 self.handlers.get(name).or_else(|| {
467 if !name.is_root() {
468 let name = name.base_name();
469 self.find(&name)
470 } else {
471 None
472 }
473 })
474 }
475}
476
477async fn lookup<R: ResponseHandler + Unpin>(
478 request_info: RequestInfo<'_>,
479 handlers: &[Arc<dyn ZoneHandler>],
480 request: &Request,
481 response_edns: Option<&Edns>,
482 mut response_handle: R,
483 #[cfg(feature = "metrics")] metrics: &CatalogMetrics,
484) -> ResponseInfo {
485 let edns = request.edns.as_ref();
486 let lookup_options = LookupOptions::from_edns(edns);
487 let request_id = request.metadata.id;
488
489 if lookup_options.dnssec_ok {
490 info!("request: {request_id} lookup_options: {lookup_options:?}");
491 }
492
493 let query = request_info.query;
494
495 for (index, handler) in handlers.iter().enumerate() {
496 debug!(
497 "performing {query} on zone handler {origin} with request id {request_id}",
498 origin = handler.origin(),
499 );
500
501 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
504 let (mut result, mut signer) = handler.search(request, lookup_options).await;
505 #[cfg(feature = "metrics")]
506 metrics.update_zone_lookup(handler.as_ref(), &result);
507
508 if let LookupControlFlow::Skip = result {
509 trace!("catalog::lookup: zone handler did not handle request");
510 continue;
511 } else if result.is_continue() {
512 trace!("catalog::lookup: zone handler did handle request with continue");
513
514 for (continue_index, consult_handler) in handlers.iter().enumerate() {
517 if continue_index == index {
518 trace!("skipping current zone handler consult (index {continue_index})");
519 continue;
520 } else {
521 trace!("calling zone handler consult (index {continue_index})");
522 }
523
524 let (new_result, new_signer) = consult_handler
525 .consult(
526 request_info.query.name(),
527 request_info.query.query_type(),
528 Some(&request_info),
529 LookupOptions::from_edns(response_edns),
530 result,
531 )
532 .await;
533 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
534 if let Some(new_signer) = new_signer {
535 signer = Some(new_signer);
536 }
537 result = new_result;
538 }
539 } else {
540 trace!("catalog::lookup: zone handler did handle request with break");
541 }
542
543 let Some(result) = result.map_result() else {
546 error!("impossible skip detected after final lookup result");
547 return send_error_response(
548 request,
549 ResponseCode::ServFail,
550 response_edns,
551 response_handle,
552 )
553 .await;
554 };
555
556 let response_message = build_response(
557 result,
558 &**handler,
559 request_id,
560 &request.metadata,
561 query,
562 edns,
563 )
564 .await;
565
566 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
567 let mut message_response = MessageResponseBuilder::new(&request.queries, response_edns)
568 .build(
569 response_message.metadata,
570 response_message.answers.iter(),
571 response_message.authorities.iter(),
572 iter::empty(),
573 response_message.additionals.iter(),
574 );
575
576 #[cfg(feature = "__dnssec")]
577 if let Some(signer) = signer {
578 let mut tbs_response_buf = Vec::with_capacity(512);
579 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
580 let tbs_response = MessageResponseBuilder::new(&request.queries, response_edns).build(
581 response_message.metadata,
582 response_message.answers.iter(),
583 response_message.authorities.iter(),
584 iter::empty(),
585 response_message.additionals.iter(),
586 );
587 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
588 error!(%error, "error encoding response");
589 return send_error_response(
590 request,
591 ResponseCode::ServFail,
592 response_edns,
593 response_handle,
594 )
595 .await;
596 }
597 match signer.sign(&tbs_response_buf) {
598 Ok(signature) => message_response.set_signature(signature),
599 Err(error) => {
600 error!(%error, "error signing response");
601 return send_error_response(
602 request,
603 ResponseCode::ServFail,
604 response_edns,
605 response_handle,
606 )
607 .await;
608 }
609 }
610 }
611
612 #[cfg(feature = "metrics")]
613 metrics.update_request_response(query, response_message.answers.iter());
614
615 match response_handle.send_response(message_response).await {
616 Err(error) => {
617 error!(%error, "error sending response");
618 return ResponseInfo::serve_failed(request);
619 }
620 Ok(response_info) => return response_info,
621 }
622 }
623
624 error!("end of chained zone handler loop reached with all zone handlers not answering");
625 send_error_response(
626 request,
627 ResponseCode::ServFail,
628 response_edns,
629 response_handle,
630 )
631 .await
632}
633
634async fn zone_transfer(
635 request_info: RequestInfo<'_>,
636 handlers: &[Arc<dyn ZoneHandler>],
637 request: &Request,
638 response_edns: Option<&Edns>,
639 now: u64,
640 mut response_handle: impl ResponseHandler,
641) -> ResponseInfo {
642 let request_edns = request.edns.as_ref();
643 let lookup_options = LookupOptions::from_edns(request_edns);
644 for handler in handlers.iter() {
645 debug!(
646 query = %request_info.query,
647 origin = %handler.origin(),
648 request_id = request.metadata.id,
649 "performing zone transfer"
650 );
651 #[cfg_attr(not(feature = "__dnssec"), expect(unused))]
652 let Some((result, signer)) = handler.zone_transfer(request, lookup_options, now).await
653 else {
654 continue;
655 };
656
657 let mut response_meta = Metadata::response_from_request(&request.metadata);
658 let zone_transfer = match result {
659 Ok(zone_transfer) => {
660 response_meta.response_code = ResponseCode::NoError;
661 response_meta.authoritative = true;
662 Some(zone_transfer)
663 }
664 Err(e) => {
665 match e {
666 LookupError::ResponseCode(
667 rcode @ ResponseCode::Refused | rcode @ ResponseCode::NotAuth,
668 ) => {
669 response_meta.response_code = rcode;
670 }
671 _ => {
672 if e.is_nx_domain() {
673 response_meta.response_code = ResponseCode::NXDomain;
674 }
675 }
676 }
677 None
678 }
679 };
680
681 #[cfg_attr(not(feature = "__dnssec"), expect(unused_mut))]
683 let mut message_response = MessageResponseBuilder::new(&request.queries, response_edns)
684 .build(
685 response_meta,
686 zone_transfer
687 .iter()
688 .flat_map(|zone_transfer| zone_transfer.iter()),
689 iter::empty(),
690 iter::empty(),
691 iter::empty(),
692 );
693
694 #[cfg(feature = "__dnssec")]
695 if let Some(signer) = signer {
696 let mut tbs_response_buf = Vec::with_capacity(512);
697 let mut encoder = BinEncoder::new(&mut tbs_response_buf);
698 let tbs_response = MessageResponseBuilder::new(&request.queries, response_edns).build(
699 response_meta,
700 zone_transfer
701 .iter()
702 .flat_map(|zone_transfer| zone_transfer.iter()),
703 iter::empty(),
704 iter::empty(),
705 iter::empty(),
706 );
707 if let Err(error) = tbs_response.destructive_emit(&mut encoder) {
708 error!(%error, "error encoding response");
709 return send_error_response(
710 request,
711 ResponseCode::ServFail,
712 response_edns,
713 response_handle,
714 )
715 .await;
716 }
717 match signer.sign(&tbs_response_buf) {
718 Ok(signature) => message_response.set_signature(signature),
719 Err(error) => {
720 error!(%error, "error signing response");
721 return send_error_response(
722 request,
723 ResponseCode::ServFail,
724 response_edns,
725 response_handle,
726 )
727 .await;
728 }
729 }
730 }
731
732 match response_handle.send_response(message_response).await {
733 Err(error) => {
734 error!(%error, "error sending response");
735 return ResponseInfo::serve_failed(request);
736 }
737 Ok(response_info) => return response_info,
738 }
739 }
740
741 error!("end of chained zone handler loop with all zone handlers not answering");
742 send_error_response(
743 request,
744 ResponseCode::ServFail,
745 response_edns,
746 response_handle,
747 )
748 .await
749}
750
751async fn send_error_response(
754 request: &Request,
755 response_code: ResponseCode,
756 mut response_edns: Option<&Edns>,
757 mut response_handle: impl ResponseHandler,
758) -> ResponseInfo {
759 let mut new_edns: Edns;
760 if response_code.high() != 0 {
761 if let Some(edns) = response_edns {
762 new_edns = edns.clone();
763 new_edns.set_rcode_high(response_code.high());
764 response_edns = Some(&new_edns);
765 }
766 }
767 let response = MessageResponseBuilder::new(&request.queries, response_edns)
768 .error_msg(&request.metadata, response_code);
769 match response_handle.send_response(response).await {
770 Ok(r) => r,
771 Err(error) => {
772 error!(%error, "failed to send response");
773 ResponseInfo::serve_failed(request)
774 }
775 }
776}
777
778async fn build_response(
780 result: Result<AuthLookup, LookupError>,
781 handler: &dyn ZoneHandler,
782 request_id: u16,
783 request_meta: &Metadata,
784 query: &LowerQuery,
785 edns: Option<&Edns>,
786) -> Message {
787 let lookup_options = LookupOptions::from_edns(edns);
788
789 match handler.zone_type() {
790 ZoneType::Primary | ZoneType::Secondary => {
791 build_authoritative_response(
792 result,
793 handler,
794 request_meta,
795 lookup_options,
796 request_id,
797 query,
798 )
799 .await
800 }
801 ZoneType::External => {
802 build_forwarded_response(
803 result,
804 request_meta,
805 #[cfg(feature = "__dnssec")]
806 handler.can_validate_dnssec(),
807 query,
808 lookup_options,
809 )
810 .await
811 }
812 }
813}
814
815async fn build_authoritative_response(
817 response: Result<AuthLookup, LookupError>,
818 handler: &dyn ZoneHandler,
819 request_meta: &Metadata,
820 lookup_options: LookupOptions,
821 _request_id: u16,
822 query: &LowerQuery,
823) -> Message {
824 let mut response_meta = Metadata::response_from_request(request_meta);
825 response_meta.authoritative = true;
826
827 let mut message = Message::new(
828 response_meta.id,
829 response_meta.message_type,
830 response_meta.op_code,
831 );
832 message.add_query(query.original().clone());
833
834 let answers = match response {
839 Ok(records) => {
840 response_meta.response_code = ResponseCode::NoError;
841 Some(records)
842 }
843 Err(LookupError::ResponseCode(
845 rcode @ ResponseCode::Refused | rcode @ ResponseCode::NotAuth,
846 )) => {
847 response_meta.response_code = rcode;
848 message.metadata = response_meta;
849 return message;
850 }
851 Err(e) => {
852 response_meta.response_code = if e.is_nx_domain() {
853 ResponseCode::NXDomain
854 } else {
855 ResponseCode::NoError
856 };
857 None
858 }
859 };
860
861 #[cfg_attr(not(feature = "__dnssec"), allow(unused_variables))]
862 let (ns, soa) = if let Some(answers) = &answers {
863 if query.query_type().is_soa() {
865 let future = handler.lookup(handler.origin(), RecordType::NS, None, lookup_options);
869 match future.await.map_result() {
870 Some(Ok(ns)) => (Some(ns), None),
871 Some(Err(error)) => {
872 warn!(%error, "ns_lookup errored");
873 (None, None)
874 }
875 None => {
876 warn!("ns_lookup unexpected skip");
877 (None, None)
878 }
879 }
880 } else {
881 #[cfg(feature = "__dnssec")]
882 {
883 let has_wildcard_match = answers.iter().any(|rr| match &rr.data {
884 RData::DNSSEC(DNSSECRData::RRSIG(rrsig)) => {
885 rrsig.input().num_labels < rr.name.num_labels()
886 }
887 _ => false,
888 });
889
890 let res = match handler.nx_proof_kind() {
891 Some(NxProofKind::Nsec3 {
892 algorithm,
893 salt,
894 iterations,
895 opt_out: _,
896 }) => handler
897 .nsec3_records(
898 Nsec3QueryInfo {
899 qname: query.name(),
900 qtype: query.query_type(),
901 has_wildcard_match,
902 algorithm: *algorithm,
903 salt,
904 iterations: *iterations,
905 },
906 lookup_options,
907 )
908 .await
909 .map_result(),
910 Some(NxProofKind::Nsec) if has_wildcard_match => handler
911 .nsec_records(query.name(), lookup_options)
912 .await
913 .map_result(),
914 _ => None,
915 };
916
917 match res {
918 Some(Ok(nsecs)) => (Some(nsecs), None),
920 Some(Err(error)) => {
921 warn!(%error, request_id = _request_id, "failed to lookup nsecs for request");
922 (None, None)
923 }
924 None => {
925 warn!(
926 request_id = _request_id,
927 "unexpected lookup skip for request"
928 );
929 (None, None)
930 }
931 }
932 }
933 #[cfg(not(feature = "__dnssec"))]
934 (None, None)
935 }
936 } else {
937 let nsecs = if lookup_options.dnssec_ok {
938 #[cfg(feature = "__dnssec")]
939 {
940 debug!("request: {_request_id} non-existent adding nsecs");
942 match handler.nx_proof_kind() {
943 Some(nx_proof_kind) => {
944 let future = match nx_proof_kind {
946 NxProofKind::Nsec => handler.nsec_records(query.name(), lookup_options),
947 NxProofKind::Nsec3 {
948 algorithm,
949 salt,
950 iterations,
951 opt_out: _,
952 } => handler.nsec3_records(
953 Nsec3QueryInfo {
954 qname: query.name(),
955 qtype: query.query_type(),
956 has_wildcard_match: false,
957 algorithm: *algorithm,
958 salt,
959 iterations: *iterations,
960 },
961 lookup_options,
962 ),
963 };
964
965 match future.await.map_result() {
966 Some(Ok(nsecs)) => Some(nsecs),
968 Some(Err(error)) => {
969 warn!(%error, request_id = _request_id, "failed to lookup nsecs for request");
970 None
971 }
972 None => {
973 warn!(
974 request_id = _request_id,
975 "unexpected lookup skip for request"
976 );
977 None
978 }
979 }
980 }
981 None => None,
982 }
983 }
984 #[cfg(not(feature = "__dnssec"))]
985 None
986 } else {
987 None
988 };
989
990 let future = handler.lookup(handler.origin(), RecordType::SOA, None, lookup_options);
991 match future.await.map_result() {
992 Some(Ok(soa)) => (nsecs, Some(soa)),
993 Some(Err(error)) => {
994 warn!(%error, "failed to lookup soa");
995 (nsecs, None)
996 }
997 None => {
998 warn!("unexpected lookup skip");
999 (None, None)
1000 }
1001 }
1002 };
1003
1004 message.metadata = response_meta;
1006
1007 if let Some(mut lookup_records) = answers {
1008 if let Some(adds) = lookup_records.take_additionals() {
1009 message.additionals.extend(adds.iter().cloned());
1010 }
1011
1012 let is_referral = lookup_records.iter().next().is_some_and(|r| {
1013 r.record_type() == RecordType::NS
1014 && query.query_type() != RecordType::NS
1015 && query.query_type() != RecordType::ANY
1016 });
1017
1018 if is_referral {
1019 message.authorities.extend(lookup_records.iter().cloned());
1020 } else {
1021 message.answers.extend(lookup_records.iter().cloned());
1022 }
1023 }
1024
1025 if let Some(ns_records) = ns {
1026 message.authorities.extend(ns_records.iter().cloned());
1027 }
1028
1029 if let Some(soa_records) = soa {
1030 message.authorities.extend(soa_records.iter().cloned());
1031 }
1032
1033 message
1034}
1035
1036async fn build_forwarded_response(
1038 response: Result<AuthLookup, LookupError>,
1039 request_meta: &Metadata,
1040 #[cfg(feature = "__dnssec")] can_validate_dnssec: bool,
1041 query: &LowerQuery,
1042 lookup_options: LookupOptions,
1043) -> Message {
1044 let mut response_meta = Metadata::response_from_request(request_meta);
1045 response_meta.recursion_available = true;
1046 response_meta.authoritative = false;
1047 let mut message = Message::new(
1048 response_meta.id,
1049 response_meta.message_type,
1050 response_meta.op_code,
1051 );
1052 message.add_query(query.original().clone());
1053
1054 if !request_meta.recursion_desired {
1055 info!(
1056 id = request_meta.id,
1057 "request disabled recursion, returning REFUSED"
1058 );
1059
1060 response_meta.response_code = ResponseCode::Refused;
1061 message.metadata = response_meta;
1062 return message;
1063 }
1064
1065 #[derive(Default)]
1066 struct ResponseParts {
1067 answers: AuthLookup,
1068 soa: Option<Record>,
1069 authorities: AuthLookup,
1070 additionals: AuthLookup,
1071 #[cfg(feature = "__dnssec")]
1072 nsec_proof: Option<Proof>,
1073 }
1074
1075 #[cfg_attr(not(feature = "__dnssec"), allow(unused_mut))]
1076 let mut rsp = match response {
1077 #[cfg(feature = "resolver")]
1078 Ok(AuthLookup::Resolved(lookup)) => {
1079 let answers =
1081 AuthLookup::answers(LookupRecords::Section(lookup.answers().to_vec()), None);
1082 let authorities =
1083 AuthLookup::answers(LookupRecords::Section(lookup.authorities().to_vec()), None);
1084 let additionals =
1085 AuthLookup::answers(LookupRecords::Section(lookup.additionals().to_vec()), None);
1086
1087 ResponseParts {
1088 answers,
1089 authorities,
1090 additionals,
1091 ..ResponseParts::default()
1092 }
1093 }
1094 Ok(answers) => ResponseParts {
1095 answers,
1096 ..ResponseParts::default()
1097 },
1098 Err(e) if e.is_no_records_found() || e.is_nx_domain() => {
1099 debug!(error = ?e, "error resolving");
1100
1101 if e.is_nx_domain() {
1102 response_meta.response_code = ResponseCode::NXDomain;
1103 }
1104
1105 let authorities = if let Some(authorities) = e.authorities() {
1107 let authorities = authorities
1108 .iter()
1109 .filter_map(|record| {
1110 if record.name == **query.name() {
1114 debug!(
1115 query_name = %query.name(),
1116 ?record,
1117 "changing response code from NXDomain to NoError due to other record",
1118 );
1119 response_meta.response_code = ResponseCode::NoError;
1120 }
1121
1122 match record.record_type() {
1123 RecordType::SOA => None,
1124 _ => Some(record.clone()),
1125 }
1126 })
1127 .collect();
1128
1129 AuthLookup::answers(LookupRecords::Section(authorities), None)
1130 } else {
1131 AuthLookup::default()
1132 };
1133
1134 if let Some(soa) = e.into_soa() {
1135 let soa = soa.into_record_of_rdata();
1136
1137 ResponseParts {
1138 soa: Some(soa),
1139 authorities,
1140 ..ResponseParts::default()
1141 }
1142 } else {
1143 ResponseParts {
1144 authorities,
1145 ..ResponseParts::default()
1146 }
1147 }
1148 }
1149 #[cfg(all(feature = "__dnssec", feature = "recursor"))]
1150 Err(LookupError::RecursiveError(recursor::RecursorError::Net(NetError::Dns(
1151 DnsError::Nsec {
1152 response, proof, ..
1153 },
1154 )))) if proof.is_insecure() => {
1155 response_meta.response_code = response.response_code;
1156
1157 if let Some(soa) = response.soa() {
1158 let soa = soa.to_owned().into_record_of_rdata();
1159
1160 ResponseParts {
1161 soa: Some(soa),
1162 nsec_proof: Some(proof),
1163 ..ResponseParts::default()
1164 }
1165 } else {
1166 ResponseParts {
1167 nsec_proof: Some(proof),
1168 ..ResponseParts::default()
1169 }
1170 }
1171 }
1172 #[cfg(feature = "__dnssec")]
1173 Err(LookupError::NetError(NetError::Dns(DnsError::DnssecBogus))) => {
1174 response_meta.response_code = ResponseCode::ServFail;
1175 ResponseParts {
1176 nsec_proof: Some(Proof::Bogus),
1177 ..ResponseParts::default()
1178 }
1179 }
1180 Err(e) => {
1181 response_meta.response_code = ResponseCode::ServFail;
1182 debug!(error = ?e, "error resolving");
1183 ResponseParts::default()
1184 }
1185 };
1186
1187 #[cfg(feature = "__dnssec")]
1189 if can_validate_dnssec {
1190 if rsp.soa.is_none() {
1215 match summarize_proofs(rsp.answers.iter(), rsp.nsec_proof) {
1216 DnssecSummary::Secure
1217 if (request_meta.authentic_data || lookup_options.dnssec_ok) =>
1218 {
1219 trace!("setting ad header");
1220 response_meta.authentic_data = true;
1221 }
1222 DnssecSummary::Bogus if !request_meta.checking_disabled => {
1223 response_meta.response_code = ResponseCode::ServFail;
1224 rsp.answers = AuthLookup::default();
1226 }
1227 _ => {}
1228 }
1229 } else {
1230 let dnssec_summary =
1231 summarize_proofs(rsp.soa.iter().chain(rsp.authorities.iter()), rsp.nsec_proof);
1232 match dnssec_summary {
1233 DnssecSummary::Secure
1234 if (request_meta.authentic_data || lookup_options.dnssec_ok) =>
1235 {
1236 trace!("setting ad header");
1237 response_meta.authentic_data = true;
1238 }
1239 DnssecSummary::Bogus if !request_meta.checking_disabled => {
1240 response_meta.response_code = ResponseCode::ServFail;
1241 trace!("clearing SOA record from response");
1243 rsp.soa = None;
1244 }
1245 _ => {}
1246 }
1247 }
1248 }
1249
1250 message.metadata = response_meta;
1251
1252 message.answers.extend(rsp.answers.iter().cloned());
1253 if let Some(soa) = rsp.soa {
1254 message.authorities.push(soa);
1255 }
1256 message.authorities.extend(rsp.authorities.iter().cloned());
1257 message.additionals.extend(rsp.additionals.iter().cloned());
1258
1259 message.maybe_strip_dnssec_records(lookup_options.dnssec_ok)
1261}
1262
1263#[cfg(feature = "__dnssec")]
1265fn summarize_proofs<'a>(
1266 records: impl Iterator<Item = &'a Record>,
1267 nsec_proof: Option<Proof>,
1268) -> DnssecSummary {
1269 let mut dnssec_summary = DnssecSummary::from_records(records);
1270 if let Some(proof) = nsec_proof {
1271 dnssec_summary = update_dnssec_summary_with_proof(dnssec_summary, proof);
1272 }
1273 dnssec_summary
1274}
1275
1276#[cfg(feature = "__dnssec")]
1278fn update_dnssec_summary_with_proof(summary: DnssecSummary, proof: Proof) -> DnssecSummary {
1279 match (summary, proof) {
1280 (DnssecSummary::Secure, Proof::Secure) => DnssecSummary::Secure,
1281 (DnssecSummary::Bogus, _) | (_, Proof::Bogus) => DnssecSummary::Bogus,
1282 (
1283 DnssecSummary::Secure | DnssecSummary::Insecure,
1284 Proof::Secure | Proof::Insecure | Proof::Indeterminate,
1285 ) => DnssecSummary::Insecure,
1286 }
1287}
1288
1289#[cfg(all(test, feature = "resolver"))]
1290mod tests {
1291 use std::{net::Ipv4Addr, str::FromStr};
1292
1293 use super::*;
1294 use crate::net::runtime::TokioRuntimeProvider;
1295 use crate::proto::rr::rdata::NS;
1296 use crate::proto::{
1297 op::{MessageType, OpCode, Query},
1298 rr::{
1299 Name, RData, Record, RecordSet, RecordType,
1300 rdata::{A, SOA},
1301 },
1302 };
1303 use crate::resolver::lookup::Lookup;
1304 use crate::store::in_memory::InMemoryZoneHandler;
1305 use crate::zone_handler::AxfrPolicy;
1306
1307 #[tokio::test]
1308 async fn test_build_forwarded_response_preserves_sections() {
1309 let query = Query::query(Name::from_str("example.com.").unwrap(), RecordType::A);
1311
1312 let mut lookup =
1314 Lookup::from_rdata(query.clone(), RData::A(A(Ipv4Addr::new(192, 0, 2, 1))));
1315
1316 lookup.extend_authorities([Record::from_rdata(
1318 Name::from_str("example.com.").unwrap(),
1319 3600,
1320 RData::SOA(SOA::new(
1321 Name::from_str("ns.example.com.").unwrap(),
1322 Name::from_str("admin.example.com.").unwrap(),
1323 1,
1324 3600,
1325 1800,
1326 604800,
1327 86400,
1328 )),
1329 )]);
1330
1331 lookup.extend_additionals([Record::from_rdata(
1333 Name::from_str("ns.example.com.").unwrap(),
1334 3600,
1335 RData::A(A(Ipv4Addr::new(192, 0, 2, 2))),
1336 )]);
1337
1338 let auth_lookup = AuthLookup::Resolved(lookup);
1340
1341 let mut request_meta = Metadata::new(1234, MessageType::Query, OpCode::Query);
1343 request_meta.recursion_desired = true;
1344 let query_lower = LowerQuery::query(query);
1345
1346 let message = build_forwarded_response(
1347 Ok(auth_lookup),
1348 &request_meta,
1349 #[cfg(feature = "__dnssec")]
1350 false,
1351 &query_lower,
1352 LookupOptions::default(),
1353 )
1354 .await;
1355
1356 assert!(
1358 !message.answers.is_empty(),
1359 "Answers section should not be empty"
1360 );
1361
1362 let authorities_count = message.authorities.len();
1364 assert!(
1365 authorities_count > 0,
1366 "Authorities section should not be empty, got {} records",
1367 authorities_count
1368 );
1369
1370 let additionals_count = message.additionals.len();
1372 assert!(
1373 additionals_count > 0,
1374 "Additionals section should not be empty, got {} records",
1375 additionals_count
1376 );
1377 }
1378
1379 #[tokio::test]
1380 async fn test_build_authoritative_response_referral() {
1381 let origin = Name::from_str("example.com.").unwrap();
1382 let sub = Name::from_str("sub.example.com.").unwrap();
1383 let ns_name = Name::from_str("ns.example.com.").unwrap();
1384
1385 let ns_record = Record::from_rdata(sub.clone(), 3600, RData::NS(NS(ns_name)));
1386 let record_set = RecordSet::from(ns_record);
1387
1388 let auth_lookup = AuthLookup::Records {
1389 answers: LookupRecords::new(LookupOptions::default(), Arc::new(record_set)),
1390 additionals: None,
1391 };
1392
1393 let handler = InMemoryZoneHandler::<TokioRuntimeProvider>::empty(
1394 origin.clone(),
1395 ZoneType::Primary,
1396 AxfrPolicy::Deny,
1397 #[cfg(feature = "__dnssec")]
1398 None,
1399 );
1400
1401 let metadata = Metadata::new(0, MessageType::Query, OpCode::Query);
1402 let query = LowerQuery::from(Query::query(
1403 Name::from_str("www.sub.example.com.").unwrap(),
1404 RecordType::A,
1405 ));
1406
1407 let message = build_authoritative_response(
1408 Ok(auth_lookup),
1409 &handler,
1410 &metadata,
1411 LookupOptions::default(),
1412 0,
1413 &query,
1414 )
1415 .await;
1416
1417 assert!(message.answers.is_empty());
1418 assert!(!message.authorities.is_empty());
1419 assert_eq!(message.authorities[0].record_type(), RecordType::NS);
1420 }
1421}