use crate::ca::CertificateManager;
use crate::error::Result;
use crate::interceptor::InterceptorHandler;
use crate::server::ProxyServer;
use slinger::Proxy;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct MitmConfig {
pub ca_storage_path: PathBuf,
pub enable_https_interception: bool,
pub enable_tcp_interception: bool,
pub max_connections: usize,
pub connection_timeout: u64,
pub upstream_proxy: Option<Proxy>,
}
impl Default for MitmConfig {
fn default() -> Self {
Self {
ca_storage_path: PathBuf::from(".slinger-mitm"),
enable_https_interception: true,
enable_tcp_interception: false,
max_connections: 1000,
connection_timeout: 30,
upstream_proxy: None,
}
}
}
pub struct MitmProxy {
config: MitmConfig,
cert_manager: Arc<CertificateManager>,
interceptor_handler: Arc<RwLock<InterceptorHandler>>,
}
impl MitmProxy {
pub async fn new(config: MitmConfig) -> Result<Self> {
let cert_manager = Arc::new(CertificateManager::new(&config.ca_storage_path).await?);
let interceptor_handler = Arc::new(RwLock::new(InterceptorHandler::new()));
Ok(Self {
config,
cert_manager,
interceptor_handler,
})
}
pub async fn default() -> Result<Self> {
Self::new(MitmConfig::default()).await
}
pub fn ca_cert_pem(&self) -> Result<String> {
self.cert_manager.ca_cert_pem()
}
pub fn ca_cert_path(&self) -> PathBuf {
self.cert_manager.ca_cert_path()
}
pub fn interceptor_handler(&self) -> Arc<RwLock<InterceptorHandler>> {
self.interceptor_handler.clone()
}
pub async fn start(&self, addr: &str) -> Result<()> {
let server = ProxyServer::new(
self.config.clone(),
self.cert_manager.clone(),
self.interceptor_handler.clone(),
)?;
server.run(addr).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_mitm_proxy_creation() {
let config = MitmConfig {
ca_storage_path: PathBuf::from("/tmp/test-mitm-ca"),
..Default::default()
};
let proxy = MitmProxy::new(config).await;
assert!(proxy.is_ok());
if let Ok(p) = proxy {
let ca_pem = p.ca_cert_pem();
assert!(ca_pem.is_ok());
}
}
}