Skip to main content

slinger_mitm/
proxy.rs

1//! MITM Proxy configuration and main proxy implementation
2
3use crate::ca::CertificateManager;
4use crate::error::Result;
5use crate::interceptor::InterceptorHandler;
6use crate::server::ProxyServer;
7use slinger::Proxy;
8use std::path::PathBuf;
9use std::sync::Arc;
10use tokio::sync::RwLock;
11
12/// Configuration for MITM proxy
13#[derive(Clone)]
14pub struct MitmConfig {
15  /// Path to store CA certificates
16  pub ca_storage_path: PathBuf,
17  /// Maximum concurrent connections
18  pub max_connections: usize,
19  /// Connection timeout in seconds
20  pub connection_timeout: u64,
21  /// Interceptor timeout in seconds. Each interceptor is given this duration
22  /// to process a request/response; if it exceeds the timeout it will be
23  /// skipped to avoid blocking the proxy.
24  pub interceptor_timeout_secs: u64,
25  /// Optional upstream proxy (supports HTTP, HTTPS, SOCKS5, SOCKS5h)
26  /// Example: "<socks5h://127.0.0.1:1080>" or "<http://proxy.example.com:8080>"
27  pub upstream_proxy: Option<Proxy>,
28}
29
30impl Default for MitmConfig {
31  fn default() -> Self {
32    Self {
33      ca_storage_path: PathBuf::from(".slinger-mitm"),
34      max_connections: 1000,
35      connection_timeout: 30,
36      // Default interceptor timeout: 60 seconds
37      interceptor_timeout_secs: 60,
38      upstream_proxy: None,
39    }
40  }
41}
42
43/// MITM Proxy main struct
44pub struct MitmProxy {
45  config: MitmConfig,
46  cert_manager: Arc<CertificateManager>,
47  interceptor_handler: Arc<RwLock<InterceptorHandler>>,
48}
49
50impl MitmProxy {
51  /// Create a new MITM proxy with the given configuration
52  pub async fn new(config: MitmConfig) -> Result<Self> {
53    let cert_manager = Arc::new(CertificateManager::new(&config.ca_storage_path).await?);
54    let interceptor_handler = Arc::new(RwLock::new(
55      InterceptorHandler::new().with_timeout(config.interceptor_timeout_secs),
56    ));
57
58    Ok(Self {
59      config,
60      cert_manager,
61      interceptor_handler,
62    })
63  }
64
65  /// Create a new MITM proxy with default configuration
66  pub async fn default() -> Result<Self> {
67    Self::new(MitmConfig::default()).await
68  }
69
70  /// Get the CA certificate in PEM format (async). This crate no longer
71  /// exposes a synchronous API for reading the CA PEM.
72  pub async fn ca_cert_pem(&self) -> Result<String> {
73    self.cert_manager.ca_cert_pem().await
74  }
75
76  /// Get the CA certificate path
77  pub fn ca_cert_path(&self) -> PathBuf {
78    self.cert_manager.ca_cert_path()
79  }
80
81  /// Get a reference to the interceptor handler
82  pub fn interceptor_handler(&self) -> Arc<RwLock<InterceptorHandler>> {
83    self.interceptor_handler.clone()
84  }
85
86  /// Start the MITM proxy server on the given address
87  pub async fn start(&self, addr: &str) -> Result<()> {
88    let server = ProxyServer::new(
89      self.config.clone(),
90      self.cert_manager.clone(),
91      self.interceptor_handler.clone(),
92    )?;
93    server.run(addr).await
94  }
95}
96
97#[cfg(test)]
98mod tests {
99  use super::*;
100
101  #[tokio::test]
102  async fn test_mitm_proxy_creation() {
103    let config = MitmConfig {
104      ca_storage_path: PathBuf::from("/tmp/test-mitm-ca"),
105      ..Default::default()
106    };
107
108    let proxy = MitmProxy::new(config).await;
109    assert!(proxy.is_ok());
110
111    if let Ok(p) = proxy {
112      let ca_pem = p.ca_cert_pem().await;
113      assert!(ca_pem.is_ok());
114    }
115  }
116}