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}