doh_dns/
lib.rs

1//! Library to make DNS over HTTPS requests.
2//!
3//! This DNS over HTTPS (DoH) library queries public DoH servers provided by Google and
4//! Clouflare. It is based on `async/await` with the help of `hyper` and `tokio`.
5//!
6//! The library supports timeouts and retries which can be fully customized. A utility
7//! is provided in this crate to use this library in the command line.
8//!
9//! # Quick Start
10//!
11//! To quickly get started, a default client can be created with `Dns:default` and
12//! `A` records can be queried using [Dns::resolve_a]. The default resolvers use Google
13//! first with a timeout of 3 seconds and Clouflare second with a timeout of 10 seconds.
14//! *Note: Cloudlare does not support queries for `ANY` records. You can use the Google
15//! resolver for that.
16//!
17//! # Example
18//! ```
19//! use doh_dns::{client::HyperDnsClient, Dns, DnsHttpsServer};
20//! use std::time::Duration;
21//! use tokio;
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
25//!     // The following sets up the main DoH server to be Google with a default timeout
26//!     // of 2 seconds. If a retry is needed, the Cloudflare's 1.1.1.1 server is used.
27//!     // Alternatively, the default server setup can be used with:
28//!     // let dns = Dns::default();
29//!     let dns: Dns<HyperDnsClient> = Dns::with_servers(&[
30//!         DnsHttpsServer::Google(Duration::from_secs(2)),
31//!         DnsHttpsServer::Cloudflare1_1_1_1(Duration::from_secs(10)),
32//!     ])
33//!     .unwrap();
34//!     match dns.resolve_a("memo.com").await {
35//!         Ok(responses) => {
36//!             if responses.is_empty() {
37//!                 println!("No entries found.");
38//!             } else {
39//!                 for res in responses {
40//!                     println!(
41//!                         "name: {}, type: {}, TTL: {}, data: {}",
42//!                         res.name,
43//!                         dns.rtype_to_name(res.r#type),
44//!                         res.TTL,
45//!                         res.data
46//!                     );
47//!                 }
48//!             }
49//!         }
50//!         Err(err) => println!("Error: {}", err),
51//!     }
52//!     Ok(())
53//! }
54//! ```
55//!
56//! # Logging
57//! This library uses the `log` crate to log errors during retries. Please see that create
58//! on methods on display such errors. If no logger is setup, nothing will be logged.
59#![feature(proc_macro_hygiene)]
60#![feature(stmt_expr_attributes)]
61pub mod client;
62mod dns;
63pub mod error;
64pub mod status;
65#[macro_use]
66extern crate serde_derive;
67extern crate num;
68#[macro_use]
69extern crate num_derive;
70use std::time::Duration;
71
72/// The data associated for requests returned by the DNS over HTTPS servers.
73#[allow(non_snake_case)]
74#[derive(Deserialize, Debug, Serialize, Clone)]
75pub struct DnsAnswer {
76    /// The name of the record.
77    pub name: String,
78    /// The type associated with each record. To convert to a string representation use
79    /// [Dns::rtype_to_name].
80    pub r#type: u32,
81    /// The time to live in seconds for this record.
82    pub TTL: u32,
83    /// The data associated with the record.
84    pub data: String,
85}
86
87#[allow(non_snake_case)]
88#[derive(Deserialize, Debug, Serialize)]
89struct DnsResponse {
90    Status: u32,
91    Answer: Option<Vec<DnsAnswer>>,
92    Comment: Option<String>,
93}
94
95/// The list of DNS over HTTPS servers allowed to query with their respective timeouts.
96/// These servers are given to [Dns::with_servers] in order of priority. Only subsequent
97/// servers are used if the request needs to be retried.
98#[derive(Clone)]
99pub enum DnsHttpsServer {
100    /// Googe's DoH server. Unfortunately, Google doesn't allow to query `8.8.8.8` or
101    /// `8.8.4.4` directly. It needs the hostname `dns.google`. If this option is
102    /// given, `8.8.8.8` and `8.8.4.4` will be used in round robin form for each new
103    /// connection.
104    Google(Duration),
105    /// Cloudflare's `1.1.1.1` DOH server. Cloudflare does not respond to `ANY` Dns
106    /// requests so [Dns::resolve_any] will always return an error.
107    Cloudflare1_1_1_1(Duration),
108    /// Cloudflare's `1.0.0.1` DOH server. Cloudflare does not respond to `ANY` Dns
109    /// requests so [Dns::resolve_any] will always return an error.
110    Cloudflare1_0_0_1(Duration),
111}
112
113impl DnsHttpsServer {
114    fn uri(&self) -> &str {
115        match self {
116            Self::Google(_) => "https://dns.google/resolve",
117            Self::Cloudflare1_1_1_1(_) => "https://1.1.1.1/dns-query",
118            Self::Cloudflare1_0_0_1(_) => "https://1.0.0.1/dns-query",
119        }
120    }
121    fn timeout(&self) -> Duration {
122        match self {
123            Self::Google(t) => *t,
124            Self::Cloudflare1_1_1_1(t) => *t,
125            Self::Cloudflare1_0_0_1(t) => *t,
126        }
127    }
128}
129
130/// The main interface to this library. It provides all functions to query records.
131pub struct Dns<C: client::DnsClient> {
132    client: C,
133    servers: Vec<DnsHttpsServer>,
134}