//! # Proxy Module
//!
//! This module has the actual proxy functionality, exposed through
-//! `ProxyServer`. The proxy consists of a local unencrypted TCP stream
+//! `Server`. The proxy consists of a local unencrypted TCP stream
//! and a remote TLS stream. Messages are passed between them via two
//! threads.
//!
//! - **Client to Server Thread**: Forwards data from client -> TLS server
//! - **Serveer to Client Thread**: Forwards data from TLS server -> client
//!
-//! Finally, the ProxyServer may be shutdown by calling `.shutdown()`,
+//! Finally, the `Server` may be shutdown by calling `.shutdown()`,
//! this will stop new connections and wait for it to finish.
//!
//! # Example
//!
//! ```
//! use std::sync::Arc;
-//! use crate::configuration::ProxyConfiguration;
-//! use crate::proxy::ProxyServer;
+//! use crate::configuration::Proxy;
+//! use crate::proxy::Server;
//!
-//! let config = Arc::new(ProxyConfiguration {
+//! let config = Arc::new(Proxy {
//! protocol: "IMAP".to_string(),
//! local_port: 143,
//! remote_domain: "imap.example.com".to_string(),
//! remote_port: 993,
//! });
//!
-//! let mut server = ProxyServer::new(config);
+//! let mut server = Server::new(config);
//! // The server runs in a background thread. To shut down gracefully:
//! server.shutdown();
//! ```
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;
-use crate::configuration::ProxyConfiguration;
+use crate::configuration::Proxy;
+use crate::middleware::get as get_middleware;
/// A proxy server that listens for plaintext connections and forwards them
/// via TLS.
///
-/// Creating a new `ProxyServer` spawns a dedicated thread that:
+/// Creating a new `Server` spawns a dedicated thread that:
/// - Binds to a local port (non-blocking mode).
/// - Spawns additional threads for each incoming client connection.
/// - Manages connection-lifetime until it receives a shutdown signal.
-pub struct ProxyServer {
+pub struct Server {
running: Arc<AtomicBool>,
thread_handle: Option<JoinHandle<()>>,
}
-impl ProxyServer {
- /// Creates a new `ProxyServer` for the given `ProxyConfiguration` and
+impl Server {
+ /// Creates a new `Server` for the given `Proxy` configuration and
/// immediately starts it.
///
/// # Arguments
///
- /// * `configuration` - Shared (Arc) `ProxyConfiguration`
+ /// * `configuration` - Shared (Arc) `Proxy`
///
/// # Returns
///
- /// A `ProxyServer` instance that will keep running until its `.shutdown()`
+ /// A `Server` instance that will keep running until its `.shutdown()`
/// method is called, or an error occurs.
- pub fn new(configuration: Arc<ProxyConfiguration>) -> Self {
+ pub fn new(configuration: Arc<Proxy>) -> Self {
let running = Arc::new(AtomicBool::new(true));
let running_clone = Arc::clone(&running);
let thread_handle = spawn(move || {
- run_proxy(configuration, running_clone);
+ run_proxy(&configuration, &running_clone);
});
- ProxyServer {
+ Server {
running,
thread_handle: Some(thread_handle),
}
/// The main loop that listens for incoming (plaintext) connections on
/// `configuration.bind_address:configuration.local_port`.
-fn run_proxy(configuration: Arc<ProxyConfiguration>, running: Arc<AtomicBool>) {
+fn run_proxy(configuration: &Arc<Proxy>, running: &Arc<AtomicBool>) {
let listener = match TcpListener::bind(format!(
"{}:{}",
configuration.bind_address, configuration.local_port
while running.load(Ordering::SeqCst) {
match listener.accept() {
- Ok((stream, addr)) => {
- info!("New {} connection from {}", configuration.protocol, addr);
+ Ok((stream, address)) => {
+ info!("New {} connection from {}", configuration.protocol, address);
- let configuration_clone = Arc::clone(&configuration);
+ let configuration_clone = Arc::clone(configuration);
let handle = spawn(move || {
- handle_client(stream, configuration_clone);
+ handle_client(stream, &configuration_clone);
});
active_threads.push(handle);
}
}
/// Handles a single client connection by bridging it (plaintext) to a TLS connection.
-fn handle_client(client_stream: TcpStream, configuration: Arc<ProxyConfiguration>) {
+fn handle_client(client_stream: TcpStream, configuration: &Arc<Proxy>) {
if let Err(e) = client_stream.set_nonblocking(true) {
error!("Failed to set client stream to nonblocking: {}", e);
return;
}
+ let available_middleware = get_middleware();
+ let available_middleware_clone = Arc::clone(&available_middleware);
+
let connector = match TlsConnector::new() {
Ok(c) => c,
Err(e) => {
}
};
- let remote_addr = format!(
+ let remote_address = format!(
"{}:{}",
configuration.remote_host, configuration.remote_port
);
- let tcp_stream = match TcpStream::connect(&remote_addr) {
+ let tcp_stream = match TcpStream::connect(&remote_address) {
Ok(stream) => stream,
Err(e) => {
- error!("Failed to connect to {}: {}", remote_addr, e);
+ error!("Failed to connect to {}: {}", remote_address, e);
return;
}
};
let mut buffer = [0u8; 8192];
let mut client_reader = client_stream;
loop {
- debug!(">");
let bytes_read = match client_reader.read(&mut buffer) {
Ok(0) => break,
Ok(n) => n,
}
};
- let debug_str = String::from_utf8_lossy(&buffer[..bytes_read])
+ let mut command = buffer[..bytes_read].to_vec();
+
+ if let Ok(mut guard) = available_middleware.lock() {
+ for middleware in guard.iter_mut() {
+ command = middleware.client_message(&command);
+ }
+ }
+
+ let debug_original = String::from_utf8_lossy(&buffer[..bytes_read])
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t");
- debug!(">>> {}", debug_str);
+
+ let debug_final = String::from_utf8_lossy(&command)
+ .replace('\n', "\\n")
+ .replace('\r', "\\r")
+ .replace('\t', "\\t");
+
+ debug!(">>> {debug_original}");
+ if debug_original != debug_final {
+ debug!("### {debug_final}");
+ }
// Lock the TLS stream and write the data to server
match tls_stream_clone.lock() {
Ok(mut tls_guard) => {
- if let Err(error) = tls_guard.write_all(&buffer[..bytes_read]) {
+ if let Err(error) = tls_guard.write_all(&command) {
debug!(">>> Error writing to server: {error}");
break;
}
let mut buffer = [0u8; 8192];
let mut client_writer = client_stream_clone;
loop {
- debug!("<");
// Lock the TLS stream and read from the server
let bytes_read = match tls_stream_clone.lock() {
Ok(mut tls_guard) => match tls_guard.read(&mut buffer) {
}
};
- let debug_str = String::from_utf8_lossy(&buffer[..bytes_read])
+ let mut command = buffer[..bytes_read].to_vec();
+
+ if let Ok(mut guard) = available_middleware_clone.lock() {
+ for middleware in guard.iter_mut() {
+ command = middleware.server_message(&command);
+ }
+ }
+
+ let debug_original = String::from_utf8_lossy(&buffer[..bytes_read])
+ .replace('\n', "\\n")
+ .replace('\r', "\\r")
+ .replace('\t', "\\t");
+
+ let debug_final = String::from_utf8_lossy(&command)
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t");
- debug!("<<< {}", debug_str);
+ debug!("<<< {debug_original}");
+ debug!("### {debug_final}");
// Write decrypted data to client
- if client_writer.write_all(&buffer[..bytes_read]).is_err() {
+ if client_writer.write_all(&command).is_err() {
debug!("<<< ERR");
break;
}