donut/
response.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::{DonutError, DonutResult, ErrorKind};
20use serde::Serialize;
21use std::str;
22use tracing::{event, Level};
23use trust_dns_client::op::DnsResponse;
24use trust_dns_client::proto::serialize::binary::BinEncodable;
25use trust_dns_client::rr::{RData, Record};
26
27#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
28pub struct ResponseMetadata {
29    min_ttl: Option<u32>,
30}
31
32impl ResponseMetadata {
33    pub fn min_ttl(&self) -> Option<u32> {
34        self.min_ttl
35    }
36}
37
38impl From<&DnsResponse> for ResponseMetadata {
39    fn from(r: &DnsResponse) -> Self {
40        let min_ttl = r.answers().iter().map(|a| a.ttl()).min();
41        ResponseMetadata { min_ttl }
42    }
43}
44
45#[derive(Debug, Default, Clone)]
46pub struct ResponseEncoderJson;
47
48impl ResponseEncoderJson {
49    pub fn new() -> Self {
50        ResponseEncoderJson
51    }
52
53    pub async fn encode(&self, res: DnsResponse) -> DonutResult<(ResponseMetadata, Vec<u8>)> {
54        let questions: Vec<JsonQuestion> = res
55            .queries()
56            .iter()
57            .map(|query| JsonQuestion::new(query.name().to_utf8(), u16::from(query.query_type())))
58            .collect();
59
60        let answers: Vec<JsonAnswer> = res
61            .answers()
62            .iter()
63            .map(|record| {
64                let data = record_to_data(record);
65                JsonAnswer::new(
66                    record.name().to_utf8(),
67                    u16::from(record.record_type()),
68                    record.ttl(),
69                    data,
70                )
71            })
72            .collect();
73
74        let meta = ResponseMetadata::from(&res);
75        let bytes = serde_json::to_vec(&JsonResponse::new(
76            u16::from(res.response_code()),
77            res.truncated(),
78            res.recursion_desired(),
79            res.recursion_available(),
80            false,
81            res.checking_disabled(),
82            questions,
83            answers,
84        ))
85        .map_err(|e| DonutError::from((ErrorKind::Internal, "unable to serialize to response", Box::new(e))))?;
86
87        event!(
88            Level::TRACE,
89            message = "encoded DNS response to JSON format",
90            num_bytes = bytes.len(),
91        );
92
93        Ok((meta, bytes))
94    }
95}
96
97pub fn record_to_data(record: &Record) -> String {
98    match record.rdata() {
99        RData::A(v) => v.to_string(),
100        RData::AAAA(v) => v.to_string(),
101        RData::ANAME(v) => v.to_string(),
102        //RData::CAA(v) => ,
103        RData::CNAME(v) => v.to_utf8(),
104        RData::MX(v) => format!("{} {}", v.preference(), v.exchange()),
105        RData::NAPTR(v) => format!(
106            "{} {} \"{}\" \"{}\" \"{}\" {}",
107            v.order(),
108            v.preference(),
109            str::from_utf8(v.flags()).unwrap_or(""),
110            str::from_utf8(v.services()).unwrap_or(""),
111            str::from_utf8(v.regexp()).unwrap_or(""),
112            v.replacement(),
113        ),
114        RData::NS(v) => v.to_utf8(),
115        //RData::NULL(v) =>  ,
116        //RData::OPENPGPKEY(v) => ,
117        //RData::OPT(v) => ,
118        RData::PTR(v) => v.to_utf8(),
119        RData::SOA(v) => format!(
120            "{} {} {} {} {} {} {}",
121            v.mname(),
122            v.rname(),
123            v.serial(),
124            v.refresh(),
125            v.retry(),
126            v.expire(),
127            v.minimum(),
128        ),
129        RData::SRV(v) => format!("{} {} {} {}", v.priority(), v.weight(), v.port(), v.target()),
130        //RData::SSHFP(v) => ,
131        //RData::TLSA(v) => ,
132        RData::TXT(v) => format!(
133            "\"{}\"",
134            v.txt_data()
135                .iter()
136                .flat_map(|t| str::from_utf8(t))
137                .collect::<Vec<&str>>()
138                .concat()
139        ),
140        _ => panic!("Unexpected result: {:?}", record),
141    }
142}
143
144#[derive(Debug, Default, Clone, PartialEq, Serialize)]
145struct JsonQuestion {
146    #[serde(rename = "name")]
147    name: String,
148
149    #[serde(rename = "type")]
150    kind: u16,
151}
152
153impl JsonQuestion {
154    fn new<S>(name: S, kind: u16) -> Self
155    where
156        S: Into<String>,
157    {
158        JsonQuestion {
159            name: name.into(),
160            kind,
161        }
162    }
163}
164
165#[derive(Debug, Default, Clone, PartialEq, Serialize)]
166struct JsonAnswer {
167    #[serde(rename = "name")]
168    name: String,
169
170    #[serde(rename = "type")]
171    kind: u16,
172
173    #[serde(rename = "TTL")]
174    ttl: u32,
175
176    #[serde(rename = "data")]
177    data: String,
178}
179
180impl JsonAnswer {
181    fn new<S1, S2>(name: S1, kind: u16, ttl: u32, data: S2) -> Self
182    where
183        S1: Into<String>,
184        S2: Into<String>,
185    {
186        JsonAnswer {
187            name: name.into(),
188            kind,
189            ttl,
190            data: data.into(),
191        }
192    }
193}
194
195#[derive(Debug, Default, Clone, PartialEq, Serialize)]
196pub struct JsonResponse {
197    #[serde(rename = "Status")]
198    status: u16,
199
200    #[serde(rename = "TC")]
201    truncated: bool,
202
203    #[serde(rename = "RD")]
204    recursion_desired: bool,
205
206    #[serde(rename = "RA")]
207    recursion_available: bool,
208
209    #[serde(rename = "AD")]
210    all_validated: bool,
211
212    #[serde(rename = "CD")]
213    checking_disabled: bool,
214
215    #[serde(rename = "Question")]
216    questions: Vec<JsonQuestion>,
217
218    #[serde(rename = "Answer")]
219    answers: Vec<JsonAnswer>,
220}
221
222impl JsonResponse {
223    #[allow(clippy::too_many_arguments)]
224    fn new(
225        status: u16,
226        truncated: bool,
227        recursion_desired: bool,
228        recursion_available: bool,
229        all_validated: bool,
230        checking_disabled: bool,
231        questions: Vec<JsonQuestion>,
232        answers: Vec<JsonAnswer>,
233    ) -> Self {
234        JsonResponse {
235            status,
236            truncated,
237            recursion_desired,
238            recursion_available,
239            all_validated,
240            checking_disabled,
241            questions,
242            answers,
243        }
244    }
245}
246
247#[derive(Debug, Default, Clone)]
248pub struct ResponseEncoderWire;
249
250impl ResponseEncoderWire {
251    pub fn new() -> Self {
252        ResponseEncoderWire
253    }
254
255    pub async fn encode(&self, res: DnsResponse) -> DonutResult<(ResponseMetadata, Vec<u8>)> {
256        let meta = ResponseMetadata::from(&res);
257        let bytes = res.to_bytes()?;
258
259        event!(
260            Level::TRACE,
261            message = "encoded DNS response to wire format",
262            num_bytes = bytes.len(),
263        );
264
265        Ok((meta, bytes))
266    }
267}