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