Initial commit
Vibe Coded with Copilot to build this.
This commit is contained in:
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "tps"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "tps"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
pretty_env_logger.workspace = true
|
||||
log.workspace = true
|
||||
clap.workspace = true
|
||||
trevors_utilities.workspace = true
|
||||
colored.workspace = true
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
use clap::Parser;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::thread;
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// Simple TCP proxy that logs traffic per client
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about)]
|
||||
struct Args {
|
||||
/// Host to listen on
|
||||
#[arg(long)]
|
||||
listen_host: String,
|
||||
|
||||
/// Port to listen on
|
||||
#[arg(long)]
|
||||
listen_port: u16,
|
||||
|
||||
/// Host to connect to
|
||||
#[arg(long)]
|
||||
connect_host: String,
|
||||
|
||||
/// Port to connect to
|
||||
#[arg(long)]
|
||||
connect_port: u16,
|
||||
}
|
||||
|
||||
static CLIENT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let listen_addr = format!("{}:{}", args.listen_host, args.listen_port);
|
||||
let target_addr = format!("{}:{}", args.connect_host, args.connect_port);
|
||||
|
||||
let listener = TcpListener::bind(&listen_addr)?;
|
||||
println!("Proxy listening on {}", listen_addr);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let stream = stream?;
|
||||
let target = target_addr.clone();
|
||||
let client_id = CLIENT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = handle_client(stream, &target, client_id) {
|
||||
eprintln!("Error handling client {}: {}", client_id, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_client(mut client: TcpStream, target: &str, client_id: usize) -> std::io::Result<()> {
|
||||
let mut server = TcpStream::connect(target)?;
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_else(|_| std::time::Duration::from_secs(0))
|
||||
.as_secs();
|
||||
|
||||
let log_dir = PathBuf::from("logs");
|
||||
create_dir_all(&log_dir)?;
|
||||
let mut log_file = File::create(log_dir.join(format!("client_{}_{}.log", client_id, timestamp)))?;
|
||||
|
||||
let mut client_clone = client.try_clone()?;
|
||||
let mut server_clone = server.try_clone()?;
|
||||
|
||||
let log_file_clone = log_file.try_clone()?;
|
||||
|
||||
let client_to_server = thread::spawn(move || {
|
||||
proxy_data(&mut client_clone, &mut server, log_file_clone, true)
|
||||
});
|
||||
|
||||
let server_to_client = thread::spawn(move || {
|
||||
proxy_data(&mut server_clone, &mut client, log_file, false)
|
||||
});
|
||||
|
||||
client_to_server.join().unwrap()?;
|
||||
server_to_client.join().unwrap()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proxy_data(
|
||||
reader: &mut TcpStream,
|
||||
writer: &mut TcpStream,
|
||||
mut log_file: File,
|
||||
from_client: bool,
|
||||
) -> std::io::Result<()> {
|
||||
let mut buffer = [0u8; 1024];
|
||||
loop {
|
||||
let n = reader.read(&mut buffer)?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
writer.write_all(&buffer[..n])?;
|
||||
|
||||
let direction = if from_client { "Client -> Server" } else { "Server -> Client" };
|
||||
writeln!(log_file, "\n[{}] {} bytes", direction, n)?;
|
||||
|
||||
for chunk in buffer[..n].chunks(16) {
|
||||
let hex: String = chunk.iter().map(|b| format!("{:02X} ", b)).collect();
|
||||
let ascii: String = chunk
|
||||
.iter()
|
||||
.map(|b| if b.is_ascii_graphic() { *b as char } else { '.' })
|
||||
.collect();
|
||||
writeln!(log_file, "{:<48} {}", hex, ascii)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user