donut/
resolve.rs

1// Donut - DNS over HTTPS server
2//
3// Copyright 2019 Nick Pillitteri
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17//
18
19use crate::types::DonutResult;
20use std::fmt;
21use tracing::{event, Level};
22use trust_dns_client::client::AsyncClient;
23use trust_dns_client::op::DnsResponse;
24use trust_dns_client::proto::xfer::DnsRequest;
25use trust_dns_client::proto::DnsHandle;
26
27/// Facade over a Trust DNS `AsyncClient` instance (UDP).
28///
29/// Note that this struct is thread safe but does not implement `Clone`. It is meant to be
30/// used as part of a reference counted (`Arc`) context object that is shared between all
31/// requests, being handled on various threads.
32pub struct UdpResolver {
33    client: AsyncClient,
34}
35
36impl UdpResolver {
37    pub fn new(client: AsyncClient) -> Self {
38        UdpResolver { client }
39    }
40
41    pub async fn resolve(&self, req: DnsRequest) -> DonutResult<DnsResponse> {
42        // Note that we clone the client here because it requires a mutable reference and
43        // cloning is the simplest and way to do that (and it's reasonably performant).
44        let mut client = self.client.clone();
45        // Clone the request and use a wrapper so that we can use 'Display' and defer it
46        // until needed by the tracing library (e.g. only if log level is INFO or lower).
47        let queries = QueryDisplay::new(req.clone());
48        let res = client.send(req).await?;
49        let code = res.response_code();
50
51        event!(
52            Level::INFO,
53            queries = %queries,
54            num_queries = res.query_count(),
55            num_answers = res.answer_count(),
56            response_code = u16::from(code),
57            response_msg = %code,
58        );
59
60        Ok(res)
61    }
62}
63
64impl fmt::Debug for UdpResolver {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(f, "UdpResolver {{ client: AsyncClient(...) }}")
67    }
68}
69
70struct QueryDisplay {
71    msg: DnsRequest,
72}
73
74impl QueryDisplay {
75    fn new(msg: DnsRequest) -> Self {
76        QueryDisplay { msg }
77    }
78}
79
80impl fmt::Display for QueryDisplay {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        for (i, q) in self.msg.queries().iter().enumerate() {
83            if i > 0 {
84                let _ = write!(f, ",");
85            }
86
87            let _ = write!(f, "{{{}}}", q);
88        }
89
90        Ok(())
91    }
92}