hickory_proto/xfer/
dns_handle.rs

1// Copyright 2015-2018 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! `DnsHandle` types perform conversions of the raw DNS messages before sending the messages on the specified streams.
9
10use futures_util::stream::Stream;
11#[cfg(any(feature = "std", feature = "no-std-rand"))]
12use tracing::debug;
13
14use crate::error::*;
15use crate::op::Query;
16use crate::xfer::{DnsRequest, DnsRequestOptions, DnsResponse, SerialMessage};
17#[cfg(any(feature = "std", feature = "no-std-rand"))]
18use crate::{
19    op::{Edns, Message, MessageType, OpCode},
20    random,
21};
22
23// TODO: this should be configurable
24// > An EDNS buffer size of 1232 bytes will avoid fragmentation on nearly all current networks.
25// https://dnsflagday.net/2020/
26#[cfg(any(feature = "std", feature = "no-std-rand"))]
27const MAX_PAYLOAD_LEN: u16 = 1232;
28
29/// Implementations of Sinks for sending DNS messages
30pub trait DnsStreamHandle: 'static + Send {
31    /// Sends a message to the Handle for delivery to the server.
32    fn send(&mut self, buffer: SerialMessage) -> Result<(), ProtoError>;
33}
34
35/// A trait for implementing high level functions of DNS.
36pub trait DnsHandle: 'static + Clone + Send + Sync + Unpin {
37    /// The associated response from the response stream, this should resolve to the Response messages
38    type Response: Stream<Item = Result<DnsResponse, ProtoError>> + Send + Unpin + 'static;
39
40    /// Only returns true if and only if this DNS handle is validating DNSSEC.
41    ///
42    /// If the DnsHandle impl is wrapping other clients, then the correct option is to delegate the question to the wrapped client.
43    fn is_verifying_dnssec(&self) -> bool {
44        false
45    }
46
47    /// Allow for disabling EDNS
48    fn is_using_edns(&self) -> bool {
49        true
50    }
51
52    /// Send a message via the channel in the client
53    ///
54    /// # Arguments
55    ///
56    /// * `request` - the fully constructed Message to send, note that most implementations of
57    ///               will most likely be required to rewrite the QueryId, do no rely on that as
58    ///               being stable.
59    fn send<R: Into<DnsRequest> + Unpin + Send + 'static>(&self, request: R) -> Self::Response;
60
61    /// A *classic* DNS query
62    ///
63    /// This is identical to `query`, but instead takes a `Query` object.
64    ///
65    /// # Arguments
66    ///
67    /// * `query` - the query to lookup
68    /// * `options` - options to use when constructing the message
69    #[cfg(any(feature = "std", feature = "no-std-rand"))]
70    fn lookup(&self, query: Query, options: DnsRequestOptions) -> Self::Response {
71        debug!("querying: {} {:?}", query.name(), query.query_type());
72        self.send(build_request(query, options))
73    }
74
75    /// A *classic* DNS query
76    ///
77    /// This is identical to `query`, but instead takes a `Query` object.
78    ///
79    /// # Arguments
80    ///
81    /// * `query` - the query to lookup
82    /// * `options` - options to use when constructing the message
83    #[cfg(not(any(feature = "std", feature = "no-std-rand")))]
84    fn lookup(&self, query: Query, options: DnsRequestOptions) -> Self::Response;
85}
86
87#[cfg_attr(not(any(feature = "std", feature = "no-std-rand")), expect(unused_mut))]
88#[cfg(any(feature = "std", feature = "no-std-rand"))]
89fn build_request(mut query: Query, options: DnsRequestOptions) -> DnsRequest {
90    // build the message
91    let mut message: Message = Message::new();
92    // TODO: This is not the final ID, it's actually set in the poll method of DNS future
93    //  should we just remove this?
94    let id: u16 = random();
95    let mut original_query = None;
96
97    #[cfg(feature = "std")]
98    if options.case_randomization {
99        original_query = Some(query.clone());
100        query.name.randomize_label_case();
101    }
102
103    message
104        .add_query(query)
105        .set_id(id)
106        .set_message_type(MessageType::Query)
107        .set_op_code(OpCode::Query)
108        .set_recursion_desired(options.recursion_desired);
109
110    // Extended dns
111    if options.use_edns {
112        message
113            .extensions_mut()
114            .get_or_insert_with(Edns::new)
115            .set_max_payload(MAX_PAYLOAD_LEN)
116            .set_version(0)
117            .set_dnssec_ok(options.edns_set_dnssec_ok);
118    }
119
120    DnsRequest::new(message, options).with_original_query(original_query)
121}