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}