Skip to main content

AirLibrary/HTTP/
Client.rs

1//! HTTP Client Module with DNS Override
2//!
3//! This module provides a secured HTTP client that uses the local DNS server
4//! for all DNS resolution. This ensures that all `*.editor.land`
5//! queries go through the local Hickory DNS server, which resolves them to
6//! `127.x.x.x` addresses as a defense-in-depth measure.
7
8use std::{sync::Arc, time::Duration};
9
10use anyhow::Result;
11// Re-export types from Mist workspace dependency
12pub use Mist::Resolver::LandDnsResolver;
13pub use Mist::Resolver::{LandResolver, TokioResolver};
14
15/// Creates a secured reqwest ClientBuilder with DNS override configured.
16///
17/// This returns a `ClientBuilder` with the DNS resolver already set, allowing
18/// you to add additional configurations before calling `.build()`.
19///
20/// # Parameters
21///
22/// * `dns_port` - The port of the local DNS server (obtained from
23///   `mist::dns_port()`)
24///
25/// # Returns
26///
27/// Returns a configured `reqwest::ClientBuilder` with the local DNS resolver.
28///
29/// # Example
30///
31/// ```rust,no_run
32/// use std::time::Duration;
33///
34/// use AirLibrary::HTTP::secured_client_builder;
35/// use Mist;
36///
37/// #[tokio::main]
38/// async fn main() -> anyhow::Result<()> {
39/// 	let dns_port = mist::dns_port();
40/// 	let client = secured_client_builder(dns_port)?.timeout(Duration::from_secs(30)).build()?;
41///
42/// 	// All HTTP requests will use the local DNS server
43/// 	Ok(())
44/// }
45/// ```
46pub fn secured_client_builder(dns_port:u16) -> Result<reqwest::ClientBuilder> {
47	let resolver = Arc::new(LandDnsResolver::new(dns_port));
48
49	Ok(reqwest::Client::builder().dns_resolver(resolver))
50}
51
52/// Creates a secured reqwest Client with DNS override.
53///
54/// This client uses the local DNS server (running on the specified port)
55/// for all DNS resolution. This is a security measure to ensure that all
56/// `*.editor.land` queries go through the local Hickory DNS server,
57/// which validates that they only resolve to `127.x.x.x` addresses.
58///
59/// # Parameters
60///
61/// * `dns_port` - The port of the local DNS server (obtained from
62///   `mist::dns_port()`)
63///
64/// # Returns
65///
66/// Returns a configured `reqwest::Client` that uses the local DNS resolver.
67///
68/// # Example
69///
70/// ```rust,no_run
71/// use AirLibrary::HTTP::secured_client;
72/// use Mist;
73///
74/// #[tokio::main]
75/// async fn main() -> anyhow::Result<()> {
76/// 	let dns_port = mist::dns_port();
77/// 	let client = secured_client(dns_port)?;
78///
79/// 	// All HTTP requests will use the local DNS server
80/// 	let response = client.get("https://code.land.playform.cloud").send().await?;
81/// 	Ok(())
82/// }
83/// ```
84///
85/// # Security
86///
87/// The DNS override ensures:
88/// - All DNS queries go through the local DNS server
89/// - `*.editor.land` domains resolve only to `127.x.x.x` addresses
90/// - Protection against DNS spoofing and cache poisoning
91/// - Defense-in-depth security for the local network
92pub fn secured_client(dns_port:u16) -> Result<reqwest::Client> {
93	secured_client_builder(dns_port)?
94		.build()
95		.map_err(|e| anyhow::anyhow!("Failed to build reqwest client: {}", e))
96}
97
98/// Creates a secured reqwest Client with timeout and DNS override.
99///
100/// This client uses the local DNS server for all DNS resolution and
101/// has a default timeout configured.
102///
103/// # Parameters
104///
105/// * `dns_port` - The port of the local DNS server (obtained from
106///   `mist::dns_port()`)
107/// * `timeout` - The timeout duration for requests
108///
109/// # Returns
110///
111/// Returns a configured `reqwest::Client` with DNS override and timeout.
112///
113/// # Example
114///
115/// ```rust,no_run
116/// use std::time::Duration;
117///
118/// use AirLibrary::HTTP::secured_client_with_timeout;
119/// use Mist;
120///
121/// #[tokio::main]
122/// async fn main() -> anyhow::Result<()> {
123/// 	let dns_port = mist::dns_port();
124/// 	let client = secured_client_with_timeout(dns_port, Duration::from_secs(30))?;
125///
126/// 	// All HTTP requests will use the local DNS server with 30s timeout
127/// 	Ok(())
128/// }
129/// ```
130pub fn secured_client_with_timeout(dns_port:u16, timeout:Duration) -> Result<reqwest::Client> {
131	secured_client_builder(dns_port)?
132		.timeout(timeout)
133		.build()
134		.map_err(|e| anyhow::anyhow!("Failed to build reqwest client: {}", e))
135}
136
137#[cfg(test)]
138mod tests {
139
140	use super::*;
141
142	#[test]
143	fn test_secured_client_creation() {
144		let port = 15353;
145
146		let result = secured_client(port);
147
148		// Should succeed even if DNS server isn't running (client creation doesn't
149		// require DNS)
150		assert!(result.is_ok(), "Client creation should succeed");
151	}
152
153	#[test]
154	fn test_secured_client_builder_creation() {
155		let port = 15354;
156
157		let result = secured_client_builder(port);
158
159		assert!(result.is_ok(), "ClientBuilder creation should succeed");
160	}
161}