Skip to main content

domain/net/client/
tsig.rs

1//! A TSIG signing & verifying passthrough transport.
2//!
3//! This module provides a transport that wraps the [high-level support for
4//! signing message exchanges with TSIG][crate::tsig], thereby authenticating
5//! them.
6//!
7//! # Usage
8//!
9//! 1. Create a signing [Key].
10//! 2. Create a [Connection] that wraps an upstream connection and uses the
11//!    key.
12//! 3. [Send a request][Connection::send_request] using the connection.
13//! 4. [Receive the response][GetResponse] or responses.
14//!
15//! # How it works
16//!
17//! Requests are automatically signed with the given key and response
18//! signatures are automatically verified. On verification failure
19//! [Error::ValidationError][crate::net::client::request::Error] will be
20//! returned.
21//!
22//! <div class="warning">
23//!
24//! TSIG verification is a destructive process. It will alter the response
25//! stripping out the TSIG RR contained within the additional section and
26//! decrementing the DNS message header ARCOUNT accordingly. It may also
27//! adjust the mesage ID, in conformance with [RFC
28//! 8945](https://www.rfc-editor.org/rfc/rfc8945.html#name-dns-message).
29//!
30//! If you wish to receive the response TSIG RR intact, do **NOT** use this
31//! transport. Instead process the response records manually using a normal
32//! transport.
33//!
34//! </div>
35//!
36//! # Requirements
37//!
38//! This transport works with any upstream transports so long as they don’t
39//! modify the message once signed nor modify the response before it can be
40//! verified.
41//!
42//! Failing to do so will result in signature verification failure. For
43//! requests this will occur at the receiving server. For responses this will
44//! result in [`GetResponse`] returning
45//! [Error::ValidationError][crate::net::client::request::Error].
46#![cfg(all(feature = "tsig", feature = "unstable-client-transport"))]
47#![warn(missing_docs)]
48#![warn(clippy::missing_docs_in_private_items)]
49
50use core::ops::DerefMut;
51
52use alloc::boxed::Box;
53use alloc::sync::Arc;
54use alloc::vec::Vec;
55use core::fmt::{Debug, Formatter};
56use core::future::Future;
57use core::pin::Pin;
58
59use bytes::Bytes;
60use octseq::Octets;
61use tracing::trace;
62
63use crate::base::Message;
64use crate::base::StaticCompressor;
65use crate::base::message::CopyRecordsError;
66use crate::base::message_builder::AdditionalBuilder;
67use crate::base::wire::Composer;
68use crate::net::client::request::{
69    ComposeRequest, ComposeRequestMulti, Error, GetResponse,
70    GetResponseMulti, SendRequest, SendRequestMulti,
71};
72use crate::rdata::tsig::Time48;
73use crate::tsig::{ClientSequence, ClientTransaction, Key};
74
75/// A wrapper around [`ClientTransaction`] and [`ClientSequence`].
76///
77/// This wrapper allows us to write calling code once that invokes methods on
78/// the TSIG signer/validator which have the same name and purpose for single
79/// response vs multiple response streams, yet have distinct Rust types and so
80/// must be called on the correct type, without needing to know at the call
81/// site which of the distinct types it actually is.
82#[derive(Clone, Debug)]
83enum TsigClient<K> {
84    /// A [`ClientTransaction`] for signing a request and validating a single
85    /// response.
86    Transaction(ClientTransaction<K>),
87
88    /// A [`ClientSequence`] for signing a request and validating a single
89    /// response.
90    Sequence(ClientSequence<K>),
91}
92
93impl<K> TsigClient<K>
94where
95    K: AsRef<Key>,
96{
97    /// A helper wrapper around [`ClientTransaction::answer`] and
98    /// [`ClientSequence::answer`] that allows the appropriate method to be
99    /// invoked without needing to know which type it actually is.
100    pub fn answer<Octs>(
101        &mut self,
102        message: &mut Message<Octs>,
103        now: Time48,
104    ) -> Result<(), Error>
105    where
106        Octs: Octets + AsMut<[u8]> + ?Sized,
107    {
108        match self {
109            TsigClient::Transaction(client) => client.answer(message, now),
110            TsigClient::Sequence(client) => client.answer(message, now),
111        }
112        .map_err(Error::Authentication)
113    }
114
115    /// A helper method that allows [`ClientSequence::done`] to be called
116    /// without knowing or caring if the underlying type is actually
117    /// [`ClientTransaction`] instead (which doesn't have a `done()` method).
118    ///
119    /// Invoking this method on a [`ClientTransaction`] is harmless and has no
120    /// effect.
121    fn done(self) -> Result<(), Error> {
122        match self {
123            TsigClient::Transaction(_) => {
124                // Nothing to do.
125                Ok(())
126            }
127            TsigClient::Sequence(client) => {
128                client.done().map_err(Error::Authentication)
129            }
130        }
131    }
132}
133
134//------------ Connection -----------------------------------------------------
135
136/// A TSIG signing and verifying transport.
137///
138/// This transport signs requests and verifies responses using a provided key
139/// and upstream transport. For more information see the [module
140/// docs][crate::net::client::tsig].
141#[derive(Clone)]
142pub struct Connection<Upstream, K> {
143    /// Upstream transport to use for requests.
144    ///
145    /// The upstream transport(s) **MUST NOT** modify the request before it is
146    /// sent nor modify the response before this transport can verify it.
147    upstream: Arc<Upstream>,
148
149    /// The key to sign requests with.
150    key: K,
151}
152
153impl<Upstream, K> Connection<Upstream, K> {
154    /// Create a new tsig transport.
155    ///
156    /// After creating the transport call `send_request` via the
157    /// [`SendRequest`] or [`SendRequestMulti`] traits to send signed messages
158    /// and verify signed responses.
159    pub fn new(key: K, upstream: Upstream) -> Self {
160        Self {
161            upstream: Arc::new(upstream),
162            key,
163        }
164    }
165}
166
167//------------ SendRequest ----------------------------------------------------
168
169impl<CR, Upstream, K> SendRequest<CR> for Connection<Upstream, K>
170where
171    CR: ComposeRequest + 'static,
172    Upstream: SendRequest<RequestMessage<CR, K>> + Send + Sync + 'static,
173    K: Clone + AsRef<Key> + Send + Sync + 'static,
174{
175    fn send_request(
176        &self,
177        request_msg: CR,
178    ) -> Box<dyn GetResponse + Send + Sync> {
179        Box::new(Request::<CR, Upstream, K>::new(
180            request_msg,
181            self.key.clone(),
182            self.upstream.clone(),
183        ))
184    }
185}
186
187//------------ SendRequestMulti ----------------------------------------------------
188
189impl<CR, Upstream, K> SendRequestMulti<CR> for Connection<Upstream, K>
190where
191    CR: ComposeRequestMulti + 'static,
192    Upstream: SendRequestMulti<RequestMessage<CR, K>> + Send + Sync + 'static,
193    K: Clone + AsRef<Key> + Send + Sync + 'static,
194{
195    fn send_request(
196        &self,
197        request_msg: CR,
198    ) -> Box<dyn GetResponseMulti + Send + Sync> {
199        Box::new(Request::<CR, Upstream, K>::new_multi(
200            request_msg,
201            self.key.clone(),
202            self.upstream.clone(),
203        ))
204    }
205}
206
207//------------ Forwarder ------------------------------------------------------
208
209/// A function that can forward a request via an upstream transport.
210///
211/// This type is generic over whether the [`RequestMessage`] being sent was
212/// sent via the [`ComposeRequest`] trait or the  [`ComposeRequestMulti`]
213/// trait, which allows common logic to be used for both despite the different
214/// trait bounds required to work with them.
215type Forwarder<Upstream, CR, K> = fn(
216    &Upstream,
217    RequestMessage<CR, K>,
218    Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
219) -> RequestState<K>;
220
221/// Forward a request that should result in a single response.
222///
223/// This function forwards a [`RequestMessage`] to an upstream transport using
224/// a client that can only accept a single response, i.e. was sent via the
225/// [`ComposeRequest`] trait.
226fn forwarder<CR, K, Upstream>(
227    upstream: &Upstream,
228    msg: RequestMessage<CR, K>,
229    tsig_client: Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
230) -> RequestState<K>
231where
232    CR: ComposeRequest,
233    Upstream: SendRequest<RequestMessage<CR, K>> + Send + Sync,
234{
235    RequestState::GetResponse(upstream.send_request(msg), tsig_client)
236}
237
238/// Forward a request that may result in multiple responses.
239///
240/// This function forwards a [`RequestMessage`] to an upstream transport using
241/// a client that can accept multiple responses, i.e. was sent via the
242/// [`ComposeRequestMulti`] trait.
243fn forwarder_multi<CR, K, Upstream>(
244    upstream: &Upstream,
245    msg: RequestMessage<CR, K>,
246    tsig_client: Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
247) -> RequestState<K>
248where
249    CR: ComposeRequestMulti,
250    Upstream: SendRequestMulti<RequestMessage<CR, K>> + Send + Sync,
251{
252    RequestState::GetResponseMulti(upstream.send_request(msg), tsig_client)
253}
254
255//------------ Request --------------------------------------------------------
256
257/// The state and related properties of an in-progress request.
258struct Request<CR, Upstream, K> {
259    /// State of the request.
260    state: RequestState<K>,
261
262    /// The request message.
263    ///
264    /// Initially Some, consumed when sent.
265    request_msg: Option<CR>,
266
267    /// The TSIG key used to sign the request.
268    key: K,
269
270    /// The upstream transport of the connection.
271    upstream: Arc<Upstream>,
272}
273
274impl<CR, Upstream, K> Request<CR, Upstream, K>
275where
276    CR: ComposeRequest,
277    Upstream: SendRequest<RequestMessage<CR, K>> + Send + Sync,
278    K: Clone + AsRef<Key>,
279    Self: GetResponse,
280{
281    /// Create a new Request object.
282    fn new(request_msg: CR, key: K, upstream: Arc<Upstream>) -> Self {
283        Self {
284            state: RequestState::Init,
285            request_msg: Some(request_msg),
286            key,
287            upstream,
288        }
289    }
290}
291
292impl<CR, Upstream, K> Request<CR, Upstream, K>
293where
294    CR: Sync + Send,
295    K: Clone + AsRef<Key>,
296{
297    /// Create a new Request object.
298    fn new_multi(request_msg: CR, key: K, upstream: Arc<Upstream>) -> Self {
299        Self {
300            state: RequestState::Init,
301            request_msg: Some(request_msg),
302            key,
303            upstream,
304        }
305    }
306
307    /// This is the implementation of the get_response method.
308    ///
309    /// This function is cancel safe.
310    async fn get_response_impl(
311        &mut self,
312        upstream_sender: Forwarder<Upstream, CR, K>,
313    ) -> Result<Option<Message<Bytes>>, Error> {
314        let (response, tsig_client) = loop {
315            match &mut self.state {
316                RequestState::Init => {
317                    let tsig_client = Arc::new(std::sync::Mutex::new(None));
318
319                    let msg = RequestMessage::new(
320                        self.request_msg.take().unwrap(),
321                        self.key.clone(),
322                        tsig_client.clone(),
323                    );
324
325                    trace!("Sending request upstream...");
326                    self.state =
327                        upstream_sender(&self.upstream, msg, tsig_client);
328                    continue;
329                }
330
331                RequestState::GetResponse(request, tsig_client) => {
332                    let response = request.get_response().await?;
333                    break (Some(response), tsig_client);
334                }
335
336                RequestState::GetResponseMulti(request, tsig_client) => {
337                    let response = request.get_response().await?;
338                    break (response, tsig_client);
339                }
340
341                RequestState::Complete => {
342                    return Err(Error::StreamReceiveError);
343                }
344            }
345        };
346
347        let res = Self::validate_response(response, tsig_client)?;
348
349        if res.is_none() {
350            self.state = RequestState::Complete;
351        }
352
353        Ok(res)
354    }
355
356    /// Perform TSIG validation on the result of receiving a response.
357    ///
358    /// If no response were received, validation must still be performed in
359    /// order to verify that the final message that was received was signed
360    /// correctly. This cannot be done when receiving the final response as we
361    /// only know that it is final by trying and failing (which may involve
362    /// waiting) to receive another response.
363    ///
364    /// This function therefore takes an optional response message and a
365    /// [`TsigClient`]. The process of validating that the final response was
366    /// valid will consume the given [`TsigClient`].
367    ///
368    /// Note: Validation is a destructive process, as it strips the TSIG RR
369    /// out of the response. The given response message is consumed, altered
370    /// and returned.
371    ///
372    /// Returns:
373    /// - `Ok(Some)` when returning a successfully validated response.
374    /// - `Ok(None)` when the end of a responses stream was successfully validated.
375    /// - `Err` if validation or some other error occurred.
376    fn validate_response(
377        response: Option<Message<Bytes>>,
378        tsig_client: &mut Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
379    ) -> Result<Option<Message<Bytes>>, Error> {
380        let res = match response {
381            None => {
382                let client = tsig_client.lock().unwrap().take().unwrap();
383                client.done()?;
384                None
385            }
386
387            Some(msg) => {
388                let mut modifiable_msg =
389                    Message::from_octets(msg.as_slice().to_vec())?;
390
391                if let Some(client) = tsig_client.lock().unwrap().deref_mut()
392                {
393                    trace!("Validating TSIG for sequence reply");
394                    client.answer(&mut modifiable_msg, Time48::now())?;
395                }
396
397                let out_vec = modifiable_msg.into_octets();
398                let out_bytes = Bytes::from(out_vec);
399                let out_msg = Message::<Bytes>::from_octets(out_bytes)?;
400                Some(out_msg)
401            }
402        };
403
404        Ok(res)
405    }
406}
407
408//-- Debug
409
410impl<CR, Upstream, K> Debug for Request<CR, Upstream, K> {
411    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
412        f.debug_struct("Request").finish()
413    }
414}
415
416//--- GetResponse
417
418impl<CR, Upstream, K> GetResponse for Request<CR, Upstream, K>
419where
420    CR: ComposeRequest,
421    Upstream: SendRequest<RequestMessage<CR, K>> + Send + Sync,
422    K: Clone + AsRef<Key> + Send + Sync,
423{
424    fn get_response(
425        &mut self,
426    ) -> Pin<
427        Box<
428            dyn Future<Output = Result<Message<Bytes>, Error>>
429                + Send
430                + Sync
431                + '_,
432        >,
433    > {
434        Box::pin(async move {
435            // Unwrap the one and only response, we don't need the multiple
436            // response handling ability of [`Request::get_response_impl`].
437            self.get_response_impl(forwarder).await.map(|v| v.unwrap())
438        })
439    }
440}
441
442//--- GetResponseMulti
443
444impl<CR, Upstream, K> GetResponseMulti for Request<CR, Upstream, K>
445where
446    CR: ComposeRequestMulti,
447    Upstream: SendRequestMulti<RequestMessage<CR, K>> + Send + Sync,
448    K: Clone + AsRef<Key> + Send + Sync,
449{
450    fn get_response(
451        &mut self,
452    ) -> Pin<
453        Box<
454            dyn Future<Output = Result<Option<Message<Bytes>>, Error>>
455                + Send
456                + Sync
457                + '_,
458        >,
459    > {
460        Box::pin(self.get_response_impl(forwarder_multi))
461    }
462}
463
464//------------ RequestState ---------------------------------------------------
465
466/// State machine used by [`Request::get_response_impl`].
467///
468/// Possible flows:
469///   - Init -> GetResponse
470///   - Init -> GetResponseMulti -> Complete
471enum RequestState<K> {
472    /// Initial state, waiting to sign and send the request.
473    Init,
474
475    /// Waiting for a response to verify.
476    GetResponse(
477        Box<dyn GetResponse + Send + Sync>,
478        Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
479    ),
480
481    /// Wait for multiple responses to verify.
482    GetResponseMulti(
483        Box<dyn GetResponseMulti + Send + Sync>,
484        Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
485    ),
486
487    /// The last of multiple responses was received and verified.
488    ///
489    /// Note: This state can only be entered when processing a sequence of
490    /// responses, i.e. using [`GetResponseMulti`]. When using [`GetResponse`]
491    /// this state will not be enetered because it only calls
492    /// [`Request::get_response_impl`] once.
493    Complete,
494}
495
496//------------ RequestMessage -------------------------------------------------
497
498/// A message that can be sent using a [`Connection`].
499///
500/// This type implements the [`ComposeRequest`] and [`ComposeRequestMulti`]
501/// traits and thus is compatible with the [`SendRequest`] and
502/// [`SendRequestMulti`] traits implemented by [`Connection`].
503///
504/// This type stores the message to be sent and implements the
505/// [`ComposeRequest`] and [`ComposeRequestMulti`] traits so that when the
506/// upstream transport accesses the message via the traits that we can at that
507/// point sign the request.
508///
509/// Signing it earlier is not possible as the upstream transport may modify
510/// the request prior to sending it, e.g. to assign a message ID or to add
511/// EDNS options, and signing **MUST** be the last modification made to the
512/// message prior to sending.
513#[derive(Clone, Debug)]
514pub struct RequestMessage<CR, K>
515where
516    CR: Send + Sync,
517{
518    /// The actual request to sign.
519    request: CR,
520
521    /// The TSIG key to sign the request with.
522    key: K,
523
524    /// The TSIG signer state.
525    ///
526    /// This must be kept here as it is created only when signing the request
527    /// and is needed later when verifying responses.
528    ///
529    /// Note: It is wrapped inside an [`Arc<Mutex<T>>`] because the signing is
530    /// done in [`Request::get_response_impl`] which returns a [`Future`] and
531    /// the compiler has no way of knowing whether or not a second call to
532    /// [`Request::get_response_impl`] could be made concurrently with an
533    /// earlier invocation which has not yet completed its progression through
534    /// its async state machine, and could be "woken up" in parallel on a
535    /// different thread thus requiring that access to the signer be made
536    /// thread safe via a locking mechanism like [`Mutex`].
537    signer: Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
538}
539
540impl<CR, K> RequestMessage<CR, K>
541where
542    CR: Send + Sync,
543{
544    /// Creates a new [`RequestMessage`].
545    fn new(
546        request: CR,
547        key: K,
548        signer: Arc<std::sync::Mutex<Option<TsigClient<K>>>>,
549    ) -> Self
550    where
551        CR: Sync + Send,
552        K: Clone + AsRef<Key>,
553    {
554        Self {
555            request,
556            key,
557            signer,
558        }
559    }
560}
561
562impl<CR, K> ComposeRequest for RequestMessage<CR, K>
563where
564    CR: ComposeRequest,
565    K: Clone + Debug + Send + Sync + AsRef<Key>,
566{
567    // Used by the stream transport.
568    fn append_message<Target: Composer>(
569        &self,
570        target: Target,
571    ) -> Result<AdditionalBuilder<Target>, CopyRecordsError> {
572        let mut target = self.request.append_message(target)?;
573
574        let client = {
575            trace!(
576                "Signing single request transaction with key '{}'",
577                self.key.as_ref().name()
578            );
579            TsigClient::Transaction(
580                ClientTransaction::request(
581                    self.key.clone(),
582                    &mut target,
583                    Time48::now(),
584                )
585                .unwrap(),
586            )
587        };
588
589        *self.signer.lock().unwrap() = Some(client);
590
591        Ok(target)
592    }
593
594    fn to_vec(&self) -> Result<Vec<u8>, Error> {
595        let msg = self.to_message()?;
596        Ok(msg.as_octets().clone())
597    }
598
599    fn to_message(&self) -> Result<Message<Vec<u8>>, Error> {
600        let mut target = StaticCompressor::new(Vec::new());
601
602        self.append_message(&mut target)?;
603
604        // It would be nice to use .builder() here. But that one deletes all
605        // sections. We have to resort to .as_builder() which gives a
606        // reference and then .clone()
607        let msg = Message::from_octets(target.into_target()).expect(
608            "Message should be able to parse output from MessageBuilder",
609        );
610        Ok(msg)
611    }
612
613    fn header(&self) -> &crate::base::Header {
614        self.request.header()
615    }
616
617    fn header_mut(&mut self) -> &mut crate::base::Header {
618        self.request.header_mut()
619    }
620
621    fn set_udp_payload_size(&mut self, value: u16) {
622        self.request.set_udp_payload_size(value)
623    }
624
625    fn set_dnssec_ok(&mut self, value: bool) {
626        self.request.set_dnssec_ok(value)
627    }
628
629    fn add_opt(
630        &mut self,
631        opt: &impl crate::base::opt::ComposeOptData,
632    ) -> Result<(), crate::base::opt::LongOptData> {
633        self.request.add_opt(opt)
634    }
635
636    fn is_answer(&self, answer: &Message<[u8]>) -> bool {
637        self.request.is_answer(answer)
638    }
639
640    fn dnssec_ok(&self) -> bool {
641        self.request.dnssec_ok()
642    }
643}
644
645impl<CR, K> ComposeRequestMulti for RequestMessage<CR, K>
646where
647    CR: ComposeRequestMulti,
648    K: Clone + Debug + Send + Sync + AsRef<Key>,
649{
650    // Used by the stream transport.
651    fn append_message<Target: Composer>(
652        &self,
653        target: Target,
654    ) -> Result<AdditionalBuilder<Target>, CopyRecordsError> {
655        let mut target = self.request.append_message(target)?;
656
657        trace!(
658            "Signing streaming request sequence with key '{}'",
659            self.key.as_ref().name()
660        );
661        let client = TsigClient::Sequence(
662            ClientSequence::request(
663                self.key.clone(),
664                &mut target,
665                Time48::now(),
666            )
667            .unwrap(),
668        );
669
670        *self.signer.lock().unwrap() = Some(client);
671
672        Ok(target)
673    }
674
675    fn to_message(&self) -> Result<Message<Vec<u8>>, Error> {
676        let mut target = StaticCompressor::new(Vec::new());
677
678        self.append_message(&mut target)?;
679
680        // It would be nice to use .builder() here. But that one deletes all
681        // sections. We have to resort to .as_builder() which gives a
682        // reference and then .clone()
683        let msg = Message::from_octets(target.into_target()).expect(
684            "Message should be able to parse output from MessageBuilder",
685        );
686        Ok(msg)
687    }
688
689    fn header(&self) -> &crate::base::Header {
690        self.request.header()
691    }
692
693    fn header_mut(&mut self) -> &mut crate::base::Header {
694        self.request.header_mut()
695    }
696
697    fn set_udp_payload_size(&mut self, value: u16) {
698        self.request.set_udp_payload_size(value)
699    }
700
701    fn set_dnssec_ok(&mut self, value: bool) {
702        self.request.set_dnssec_ok(value)
703    }
704
705    fn add_opt(
706        &mut self,
707        opt: &impl crate::base::opt::ComposeOptData,
708    ) -> Result<(), crate::base::opt::LongOptData> {
709        self.request.add_opt(opt)
710    }
711
712    fn is_answer(&self, answer: &Message<[u8]>) -> bool {
713        self.request.is_answer(answer)
714    }
715
716    fn dnssec_ok(&self) -> bool {
717        self.request.dnssec_ok()
718    }
719}
720
721#[cfg(test)]
722mod tests {
723    use super::*;
724    use crate::base::iana::Rcode;
725    use crate::base::message_builder::QuestionBuilder;
726    use crate::base::{MessageBuilder, Name, Rtype};
727    use crate::tsig::{
728        Algorithm, KeyName, KeyStore, ServerSequence, ServerTransaction,
729        ValidationError,
730    };
731    use core::future::ready;
732    use core::str::FromStr;
733    use std::eprintln;
734
735    #[tokio::test]
736    async fn single_signed_valid_response() {
737        do_single_response(false).await;
738    }
739
740    #[tokio::test]
741    async fn single_signed_invalid_response() {
742        do_single_response(true).await;
743    }
744
745    async fn do_single_response(invalidate_signature: bool) {
746        // Make a query message that would be expected to result in a single
747        // reply.
748        let msg = mk_request_msg(Rtype::A);
749
750        // Wrap that message into a request message compatible with a
751        // transport capable of receving a single response.
752        let req =
753            crate::net::client::request::RequestMessage::new(msg).unwrap();
754
755        // Make a TSIG key to sign the request with.
756        let key = mk_tsig_key();
757
758        // Make a mock upstream that will "send" the request and receives a
759        // mock TSIG signed response back.
760        let upstream =
761            Arc::new(MockUpstream::new(key.clone(), invalidate_signature));
762
763        // Wrap the request message into a TSIG signing request with a signing
764        // key and upstream transport.
765        let mut req = Request::new(req, key, upstream);
766
767        // "Send" the request and receive the validated mock response.
768        let res = req.get_response().await;
769
770        assert_eq!(res.is_err(), invalidate_signature);
771
772        if let Ok(res) = res {
773            // Verify that the mock response has had its TSIG RR stripped out
774            // during validation.
775            assert_eq!(
776                res.header_counts().arcount(),
777                0,
778                "TSIG RR should have been removed from the additional section during response processing"
779            );
780        }
781    }
782
783    #[tokio::test]
784    async fn multiple_signed_valid_responses() {
785        do_multiple_responses(false, false).await
786    }
787
788    #[tokio::test]
789    async fn multiple_signed_responses_with_one_invalid() {
790        do_multiple_responses(true, false).await
791    }
792
793    #[tokio::test]
794    async fn multiple_signed_valid_responses_and_a_final_unsigned_response() {
795        do_multiple_responses(false, true).await
796    }
797
798    async fn do_multiple_responses(
799        invalidate_signature: bool,
800        dont_sign_last_response: bool,
801    ) {
802        // Make a query message that would be expected to result in multiple
803        // replies.
804        let msg = mk_request_msg(Rtype::AXFR);
805
806        // Wrap that message into a request message compatible with a
807        // transport capable of receving multiple responses.
808        let req = crate::net::client::request::RequestMessageMulti::new(msg)
809            .unwrap();
810
811        // Make a TSIG key to sign the request with.
812        let key = mk_tsig_key();
813
814        // Make a mock upstream that will "send" the request and receive
815        // multiple mock TSIG signed responses back.
816        let upstream = Arc::new(MockUpstreamMulti::new(
817            key.clone(),
818            invalidate_signature,
819            dont_sign_last_response,
820        ));
821
822        // Wrap the request message into a TSIG signing request with a signing
823        // key and upstream transport.
824        let mut req = Request::new_multi(req, key, upstream);
825
826        // "Send" the request and receive the first validated mock response.
827        let res = req
828            .get_response()
829            .await
830            .unwrap()
831            .expect("First response is missing");
832
833        // Verify that the mock response has had its TSIG RR stripped out
834        // during validation.
835        assert_eq!(
836            res.header_counts().arcount(),
837            0,
838            "TSIG RR should have been removed from the additional section during response processing"
839        );
840
841        // Receive the second mock response, which may have been deliberately
842        // invalidated.
843        let res = req.get_response().await;
844
845        if invalidate_signature {
846            assert!(
847                matches!(
848                    res,
849                    Err(Error::Authentication(ValidationError::BadSig))
850                ),
851                "Expected error BadSig but the result was: {res:?}"
852            );
853        } else {
854            assert!(res.is_ok(), "Unexpected error message: {res:?}");
855        }
856
857        if let Ok(res) = res {
858            let res = res.expect("Second response is missing");
859
860            // Verify that the mock response has had its TSIG RR stripped out
861            // during validation.
862            assert_eq!(
863                res.header_counts().arcount(),
864                0,
865                "TSIG RR should have been removed from the additional section during response processing"
866            );
867
868            // Receive the third and final mock response, which may have been
869            // deliberately not signed, in order to test whether or not we
870            // are correctly calling `ClientSequence::done()` to catch this
871            // case. This shouldn't fail at this point however as apparently
872            // it's only caught when .done() is called when no more responses
873            // are received.
874            let res = req
875                .get_response()
876                .await
877                .unwrap()
878                .expect("Third response is missing");
879
880            // Verify that the mock response has had its TSIG RR stripped out
881            // during validation, or it was never added during response
882            // generation.
883            if dont_sign_last_response {
884                assert_eq!(
885                    res.header_counts().arcount(),
886                    0,
887                    "TSIG RR should never have been added to the additional section during response generation"
888                );
889            } else {
890                assert_eq!(
891                    res.header_counts().arcount(),
892                    0,
893                    "TSIG RR should have been removed from the additional section during response processing"
894                );
895            }
896
897            if dont_sign_last_response {
898                // Attempt to receive another response but discover that the
899                // last response was not signed as it should have been.
900                assert!(
901                    matches!(
902                        req.get_response().await,
903                        Err(Error::Authentication(
904                            ValidationError::TooManyUnsigned
905                        ))
906                    ),
907                    "Receiving another response should have failed because the last response should have lacked a signature"
908                );
909            } else {
910                // Attempt to receive another response but discover that this
911                // is the end of the response sequence.
912                assert!(
913                    req.get_response().await.unwrap().is_none(),
914                    "There should not be a fourth response"
915                );
916            }
917        }
918    }
919
920    // Make a query for the given RTYPE.
921    fn mk_request_msg(rtype: Rtype) -> QuestionBuilder<Vec<u8>> {
922        let mut msg = MessageBuilder::new_vec();
923        msg.header_mut().set_rd(true);
924        msg.header_mut().set_ad(true);
925        let mut msg = msg.question();
926        msg.push((Name::vec_from_str("example.com").unwrap(), rtype))
927            .unwrap();
928        msg
929    }
930
931    // Make a TSIG key for signing test requests and responses with.
932    fn mk_tsig_key() -> Arc<Key> {
933        // Create a signing key.
934        let key_name = KeyName::from_str("demo-key").unwrap();
935        let secret = crate::utils::base64::decode::<Vec<u8>>(
936            "zlCZbVJPIhobIs1gJNQfrsS3xCxxsR9pMUrGwG8OgG8=",
937        )
938        .unwrap();
939        Arc::new(
940            Key::new(Algorithm::Sha256, &secret, key_name, None, None)
941                .unwrap(),
942        )
943    }
944
945    //------------ MockGetResponse --------------------------------------------
946
947    #[derive(Debug)]
948    struct MockGetResponse<CR, KS> {
949        request_msg: CR,
950        key_store: KS,
951        invalidate_signature: bool,
952    }
953
954    impl<CR, KS> MockGetResponse<CR, KS> {
955        fn new(
956            request_msg: CR,
957            key_store: KS,
958            invalidate_signature: bool,
959        ) -> Self {
960            Self {
961                request_msg,
962                key_store,
963                invalidate_signature,
964            }
965        }
966    }
967
968    //--- GetResponse
969
970    impl<CR: ComposeRequest + Debug, KS: Debug + KeyStore> GetResponse
971        for MockGetResponse<CR, KS>
972    {
973        fn get_response(
974            &mut self,
975        ) -> Pin<
976            Box<
977                dyn Future<Output = Result<Message<Bytes>, Error>>
978                    + Send
979                    + Sync
980                    + '_,
981            >,
982        > {
983            let mut req = self.request_msg.to_message().unwrap();
984
985            // Create a TSIG signer for a single response based on the
986            // received request.
987            let tsig = ServerTransaction::request(
988                &self.key_store,
989                &mut req,
990                Time48::now(),
991            )
992            .unwrap()
993            .unwrap();
994
995            // Generate a mock response to the request.
996            let builder = MessageBuilder::new_bytes();
997            let builder = builder.start_answer(&req, Rcode::NOERROR).unwrap();
998            let mut builder = builder.additional();
999
1000            // Sign the response.
1001            tsig.answer(&mut builder, Time48::now()).unwrap();
1002
1003            if self.invalidate_signature {
1004                // Invalidate the signature.
1005                builder.header_mut().set_rcode(Rcode::SERVFAIL);
1006            }
1007
1008            // Generate the wire format response message and sanity check it
1009            // before returning it.
1010            let res = builder.into_message();
1011            assert_eq!(
1012                res.header_counts().arcount(),
1013                1,
1014                "Constructed response lacks a TSIG RR in the additional section"
1015            );
1016            Box::pin(ready(Ok(res)))
1017        }
1018    }
1019
1020    //------------ MockGetResponseMulti ---------------------------------------
1021
1022    #[derive(Debug)]
1023    struct MockGetResponseMulti<CR, KS> {
1024        request_msg: CR,
1025        key_store: KS,
1026        sent_request: Option<Message<Vec<u8>>>,
1027        num_responses_generated: usize,
1028        signer: Option<ServerSequence<KS>>,
1029        invalidate_signature: bool,
1030        dont_sign_last_response: bool,
1031    }
1032
1033    impl<CR, KS> MockGetResponseMulti<CR, KS> {
1034        fn new(
1035            request_msg: CR,
1036            key_store: KS,
1037            invalidate_signature: bool,
1038            dont_sign_last_response: bool,
1039        ) -> Self {
1040            Self {
1041                request_msg,
1042                key_store,
1043                sent_request: None,
1044                num_responses_generated: 0,
1045                signer: None,
1046                invalidate_signature,
1047                dont_sign_last_response,
1048            }
1049        }
1050    }
1051
1052    //--- GetResponseMulti
1053
1054    impl<CR, KS> GetResponseMulti for MockGetResponseMulti<CR, KS>
1055    where
1056        CR: ComposeRequestMulti + Debug,
1057        KS: Debug + KeyStore<Key = KS> + AsRef<Key>,
1058    {
1059        fn get_response(
1060            &mut self,
1061        ) -> Pin<
1062            Box<
1063                dyn Future<Output = Result<Option<Message<Bytes>>, Error>>
1064                    + Send
1065                    + Sync
1066                    + '_,
1067            >,
1068        > {
1069            // Generate a sequence of at most 3 responses.
1070            if self.num_responses_generated == 3 {
1071                return Box::pin(ready(Ok(None)));
1072            }
1073
1074            self.num_responses_generated += 1;
1075
1076            // When first receiving the request, generate a TSIG signer for a
1077            // multiple response sequence based on the received request.
1078            let mut tsig = match self.signer.take() {
1079                Some(tsig) => tsig,
1080                None => {
1081                    let mut req = self.request_msg.to_message().unwrap();
1082
1083                    let tsig = ServerSequence::request(
1084                        &self.key_store,
1085                        &mut req,
1086                        Time48::now(),
1087                    )
1088                    .unwrap()
1089                    .unwrap();
1090
1091                    // Store the signer, we'll need it to sign subsequent
1092                    // responses.
1093                    self.sent_request = Some(req);
1094
1095                    tsig
1096                }
1097            };
1098
1099            // Generate a mock response to the request.
1100            let req = self.sent_request.as_ref().unwrap();
1101            let builder = MessageBuilder::new_bytes();
1102            let builder = builder.start_answer(req, Rcode::NOERROR).unwrap();
1103            let mut builder = builder.additional();
1104
1105            // Decide whether to sign and/or invalidate the response.
1106            let (sign, invalidate) = match self.num_responses_generated {
1107                1 => (true, false),
1108                2 => (true, self.invalidate_signature),
1109                3 => (!self.dont_sign_last_response, false),
1110                _ => unreachable!(),
1111            };
1112
1113            eprintln!(
1114                "Response {}: sign={}, invalidate={}",
1115                self.num_responses_generated, sign, invalidate
1116            );
1117
1118            // Sign the response (we might strip the signature out below).
1119            if sign {
1120                tsig.answer(&mut builder, Time48::now()).unwrap();
1121            }
1122
1123            // Put the signer back in storage for the next response.
1124            self.signer = Some(tsig);
1125
1126            if invalidate {
1127                // Invalidate the signature.
1128                builder.header_mut().set_rcode(Rcode::SERVFAIL);
1129            }
1130
1131            // Generate the wire format response message and sanity check it
1132            // before returning it.
1133            let res = builder.into_message();
1134            if sign {
1135                assert_eq!(
1136                    res.header_counts().arcount(),
1137                    1,
1138                    "Constructed response lacks a TSIG RR in the additional section"
1139                );
1140                let rec = res.additional().unwrap().next().unwrap().unwrap();
1141                assert_eq!(rec.rtype(), Rtype::TSIG);
1142            }
1143            Box::pin(ready(Ok(Some(res))))
1144        }
1145    }
1146
1147    //------------ MockUpstream -----------------------------------------------
1148
1149    struct MockUpstream {
1150        key: Arc<Key>,
1151        invalidate_signature: bool,
1152    }
1153
1154    impl MockUpstream {
1155        fn new(key: Arc<Key>, invalidate_signature: bool) -> Self {
1156            Self {
1157                key,
1158                invalidate_signature,
1159            }
1160        }
1161    }
1162
1163    //--- SendRequest
1164
1165    impl<CR: ComposeRequest + Debug + Send + Sync + 'static> SendRequest<CR>
1166        for MockUpstream
1167    {
1168        fn send_request(
1169            &self,
1170            request_msg: CR,
1171        ) -> Box<dyn GetResponse + Send + Sync> {
1172            Box::new(MockGetResponse::new(
1173                request_msg,
1174                self.key.clone(),
1175                self.invalidate_signature,
1176            ))
1177        }
1178    }
1179
1180    //------------ MockUpstreamMulti ------------------------------------------
1181
1182    struct MockUpstreamMulti {
1183        key: Arc<Key>,
1184        invalidate_signature: bool,
1185        dont_sign_last_response: bool,
1186    }
1187    impl MockUpstreamMulti {
1188        fn new(
1189            key: Arc<Key>,
1190            invalidate_signature: bool,
1191            dont_sign_last_response: bool,
1192        ) -> Self {
1193            Self {
1194                key,
1195                invalidate_signature,
1196                dont_sign_last_response,
1197            }
1198        }
1199    }
1200
1201    impl<CR> SendRequestMulti<CR> for MockUpstreamMulti
1202    where
1203        CR: ComposeRequestMulti + Debug + Send + Sync + 'static,
1204    {
1205        fn send_request(
1206            &self,
1207            request_msg: CR,
1208        ) -> Box<dyn GetResponseMulti + Send + Sync> {
1209            Box::new(MockGetResponseMulti::new(
1210                request_msg,
1211                self.key.clone(),
1212                self.invalidate_signature,
1213                self.dont_sign_last_response,
1214            ))
1215        }
1216    }
1217}