domain/net/client/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
#![cfg_attr(
not(feature = "unstable-client-transport"),
doc = " The `unstable-client-transport` feature is necessary to enable this module."
)]
//! Sending requests and receiving responses.
//!
//! This module provides DNS transport protocols that allow sending a DNS
//! request and receiving the corresponding reply.
//!
//! Currently the following transport protocols are supported:
//! * [dgram] DNS over a datagram protocol, typically UDP.
//! * [stream] DNS over an octet stream protocol, typically TCP or TLS.
//! Only a single connection is supported.
//! The transport works as long as the connection continues to exist.
//! * [multi_stream] This is a layer on top of [stream] where new connections
//! are established as old connections are closed (or fail).
//! * [dgram_stream] This is a combination of [dgram] and [multi_stream].
//! This is typically needed because a request over UDP can receive
//! a truncated response, which should be retried over TCP.
//! * [redundant] This transport multiplexes requests over a collection of
//! transport connections. The [redundant] transport favors the connection
//! with the lowest response time. Any of the other transports can be added
//! as upstream transports.
//! * [cache] This is a simple message cache provided as a pass through
//! transport. The cache works with any of the other transports.
#![cfg_attr(feature = "tsig", doc = "* [tsig]:")]
#![cfg_attr(not(feature = "tsig",), doc = "* tsig:")]
//! This is a TSIG request signer and response verifier provided as a
//! pass through transport. The tsig transport works with any upstream
//! transports so long as they don't modify the message once signed nor
//! modify the response before it can be verified.
#![cfg_attr(feature = "unstable-validator", doc = "* [validator]:")]
#![cfg_attr(not(feature = "unstable-validator",), doc = "* validator:")]
//! This is a DNSSEC validator provided as a pass through transport.
//! The validator works with any of the other transports.
//!
//! Sending a request and receiving the reply consists of four steps:
//! 1) Creating a request message,
//! 2) Creating a DNS transport,
//! 3) Sending the request, and
//! 4) Receiving the reply or replies.
//!
//! The first and second step are independent and can happen in any order.
//! The third step uses the resuts of the first and second step.
//! Finally, the fourth step uses the result of the third step.
//! # Creating a request message
//!
//! The DNS transport protocols expect a request message that implements the
//! [ComposeRequest][request::ComposeRequest] trait.
//! This trait allows transports to add ENDS(0) options, set flags, etc.
//! The [RequestMessage][request::RequestMessage] type implements this trait.
//! The [new][request::RequestMessage::new] method of RequestMessage creates
//! a new RequestMessage object based an existing messsage (that implements
//! ```Into<Message<Octs>>```).
//!
//! For example:
//! ```rust
//! # use domain::base::{Name, MessageBuilder, Rtype};
//! # use domain::net::client::request::RequestMessage;
//! let mut msg = MessageBuilder::new_vec();
//! msg.header_mut().set_rd(true);
//! let mut msg = msg.question();
//! msg.push(
//! (Name::vec_from_str("example.com").unwrap(), Rtype::AAAA)
//! ).unwrap();
//! let req = RequestMessage::new(msg);
//! ```
//! # Creating a DNS transport
//!
//! Creating a DNS transport typically involves creating a configuration
//! object, creating the underlying network connection, creating the
//! DNS transport and running a ```run``` method as a separate task. This
//! is illustrated in the following example:
//! ```rust
//! # use domain::net::client::multi_stream;
//! # use domain::net::client::protocol::TcpConnect;
//! # use domain::net::client::request::SendRequest;
//! # use std::net::{IpAddr, SocketAddr};
//! # use std::str::FromStr;
//! # use std::time::Duration;
//! # async fn _test() {
//! # let server_addr = SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53);
//! let mut multi_stream_config = multi_stream::Config::default();
//! multi_stream_config.stream_mut().set_response_timeout(
//! Duration::from_millis(100),
//! );
//! let tcp_connect = TcpConnect::new(server_addr);
//! let (tcp_conn, transport) = multi_stream::Connection::with_config(
//! tcp_connect, multi_stream_config
//! );
//! tokio::spawn(transport.run());
//! # let req = domain::net::client::request::RequestMessage::new(
//! # domain::base::MessageBuilder::new_vec()
//! # ).unwrap();
//! # let mut request = tcp_conn.send_request(req);
//! # }
//! ```
//! # Sending the request
//!
//! A connection implements the [SendRequest][request::SendRequest] trait.
//! This trait provides a single method,
//! [send_request][request::SendRequest::send_request] and returns an object
//! that provides the response.
//!
//! For example:
//! ```no_run
//! # use domain::net::client::request::{RequestMessageMulti, SendRequest};
//! # use std::net::{IpAddr, SocketAddr};
//! # use std::str::FromStr;
//! # async fn _test() {
//! # let (tls_conn, _) = domain::net::client::stream::Connection::<_, RequestMessageMulti<Vec<u8>>>::new(
//! # domain::net::client::protocol::TcpConnect::new(
//! # SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53)
//! # )
//! # );
//! # let req = domain::net::client::request::RequestMessage::new(
//! # domain::base::MessageBuilder::new_vec()
//! # ).unwrap();
//! let mut request = tls_conn.send_request(req);
//! # }
//! ```
//! where ```tls_conn``` is a transport connection for DNS over TLS.
//! # Receiving the response
//!
//! The [send_request][request::SendRequest::send_request] method returns an
//! object that implements the [GetResponse][request::GetResponse] trait.
//! This trait provides a single method,
//! [get_response][request::GetResponse::get_response], which returns the
//! DNS response message or an error. This method is intended to be
//! cancelation safe.
//!
//! For example:
//! ```no_run
//! # use crate::domain::net::client::request::{RequestMessageMulti, SendRequest};
//! # use std::net::{IpAddr, SocketAddr};
//! # use std::str::FromStr;
//! # async fn _test() {
//! # let (tls_conn, _) = domain::net::client::stream::Connection::<_, RequestMessageMulti<Vec<u8>>>::new(
//! # domain::net::client::protocol::TcpConnect::new(
//! # SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53)
//! # )
//! # );
//! # let req = domain::net::client::request::RequestMessage::new(
//! # domain::base::MessageBuilder::new_vec()
//! # ).unwrap();
//! # let mut request = tls_conn.send_request(req);
//! let reply = request.get_response().await;
//! # }
//! ```
//!
//! <div class="warning">
//!
//! **Support for multiple responses:**
//!
//! [RequestMessage][request::RequestMessage] is designed for the most common
//! use case: single request, single response.
//!
//! However, zone transfers (e.g. using the `AXFR` or `IXFR` query types) can
//! result in multiple responses. Attempting to create a
//! [RequestMessage][request::RequestMessage] for such a query will result in
//! [Error::FormError][request::Error::FormError].
//!
//! For zone transfers you should use
//! [RequestMessageMulti][request::RequestMessageMulti] instead which can be
//! used like so:
//!
//! ```no_run
//! # use crate::domain::net::client::request::{RequestMessage, SendRequestMulti};
//! # use std::net::{IpAddr, SocketAddr};
//! # use std::str::FromStr;
//! # async fn _test() {
//! # let (conn, _) = domain::net::client::stream::Connection::<RequestMessage<Vec<u8>>, _>::new(
//! # domain::net::client::protocol::TcpConnect::new(
//! # SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53)
//! # )
//! # );
//! # let req = domain::net::client::request::RequestMessageMulti::new(
//! # domain::base::MessageBuilder::new_vec()
//! # ).unwrap();
//! # let mut request = conn.send_request(req);
//! while let Ok(reply) = request.get_response().await {
//! // ...
//! }
//! # }
//! ```
//!
//! </div>
//!
//! # Limitations
//!
//! The current implementation has the following limitations:
//! * The [dgram] transport does not support DNS Cookies
//! ([`RFC 7873`](https://tools.ietf.org/html/rfc7873)
//! Domain Name System (DNS) Cookies).
//! * The [multi_stream] transport does not support timeouts or other limits on
//! the number of attempts to open a connection. The caller has to
//! implement a timeout mechanism.
//! * The [cache] transport does not support:
//! * Prefetching. In this context, prefetching means updating a cache entry
//! before it expires.
//! * [RFC 8767](https://tools.ietf.org/html/rfc8767)
//! (Serving Stale Data to Improve DNS Resiliency)
//! * [RFC 7871](https://tools.ietf.org/html/rfc7871)
//! (Client Subnet in DNS Queries)
//! * [RFC 8198](https://tools.ietf.org/html/rfc8198)
//! (Aggressive Use of DNSSEC-Validated Cache)
//! # Example with various transport connections
//! ```no_run
#![doc = include_str!("../../../examples/client-transports.rs")]
//! ```
#![cfg(feature = "unstable-client-transport")]
#![cfg_attr(docsrs, doc(cfg(feature = "unstable-client-transport")))]
#![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
pub mod cache;
pub mod dgram;
pub mod dgram_stream;
pub mod multi_stream;
pub mod protocol;
pub mod redundant;
pub mod request;
pub mod stream;
#[cfg(feature = "tsig")]
pub mod tsig;
#[cfg(feature = "unstable-validator")]
pub mod validator;
pub mod validator_test;