diff --git a/.gitignore b/.gitignore index c41cc9e..8ebe069 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -/target \ No newline at end of file +/target +ideas.txt +Cargo.lock \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 345e810..dc7088f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "autocfg" version = "1.0.1" @@ -22,9 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" [[package]] name = "cfg-if" @@ -32,34 +26,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "libz-sys", - "miniz_oxide", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -75,38 +41,11 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "httparse" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" - [[package]] name = "libc" -version = "0.2.99" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" - -[[package]] -name = "libz-sys" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "once_cell" @@ -149,17 +88,14 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "serde" -version = "1.0.127" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" [[package]] name = "sslrelay" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "chunked_transfer", - "flate2", - "httparse", "openssl", "toml", ] diff --git a/Cargo.toml b/Cargo.toml index cd998f6..ab7f290 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,9 @@ [package] name = "sslrelay" -version = "0.1.0" +version = "0.2.0" authors = ["PinkP4nther "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] openssl = "0.10.34" -httparse = "1.4.1" -chunked_transfer = "1.4.0" -flate2 = { version = "1.0.17", features = ["zlib"], default-features = false } toml = "0.5.8" \ No newline at end of file diff --git a/README.md b/README.md index 5b9b84d..abda71c 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ A relay I wrote to help with intercepting/modifying TLS encrypted network traffi The idea is to generate a certificate and a private key (You may need to generate a CA for your certificate, so that you can tell your system or the application to trust the generated certificate). Then use this library to continuously rewrite or display encrypted network traffic. -Right now this library is mostly written to target the HTTP over TLS however I would like to make it work seamlessly with any data over TLS/SSL. \ No newline at end of file +This library now supports continuous TCP sessions. | 09/02/2021 \ No newline at end of file diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index 991506e..bf1440c 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -1,7 +1,7 @@ use sslrelay::{self, ConfigType, RelayConfig, HandlerCallbacks}; - // Handler object +#[derive(Clone)] // Must have Clone trait implemented. struct Handler; /* @@ -10,24 +10,24 @@ struct Handler; */ impl HandlerCallbacks for Handler { - // Request non blocking callback + // DownStream non blocking callback fn ds_nb_callback(&self, _in_data: Vec) { - println!("[+] Request Non Blocking CallBack!"); + println!("[CALLBACK] Down Stream Non Blocking CallBack!"); } - // Request blocking callback + // DownStream blocking callback fn ds_b_callback(&self, _in_data: &mut Vec) { - println!("[+] Request Blocking CallBack!"); + println!("[CALLBACK] Down Stream Blocking CallBack!"); } - // Response non blocking callback + // UpStream non blocking callback fn us_nb_callback(&self, _in_data: Vec) { - println!("[+] Response Non Blocking CallBack!"); + println!("[CALLBACK] Up Stream Non Blocking CallBack!"); } - // Response blocking callback + // UpStream blocking callback fn us_b_callback(&self, _in_data: &mut Vec) { - println!("[+] Response Blocking CallBack!"); + println!("[CALLBACK] Up Stream Blocking CallBack!"); } } @@ -44,7 +44,6 @@ fn main() { remote_port: "443".to_string(), ssl_private_key_path: "./remote.com.key".to_string(), ssl_cert_path: "./remote.com.crt".to_string(), - verbose_level: 2, })); // Start listening diff --git a/examples/modifydata/Cargo.toml b/examples/modifydata/Cargo.toml new file mode 100644 index 0000000..4927f97 --- /dev/null +++ b/examples/modifydata/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "modifydata" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sslrelay = {path = "../../"} \ No newline at end of file diff --git a/examples/basic/relay_config.toml b/examples/modifydata/relay_config.toml similarity index 78% rename from examples/basic/relay_config.toml rename to examples/modifydata/relay_config.toml index 13a82e1..064379d 100644 --- a/examples/basic/relay_config.toml +++ b/examples/modifydata/relay_config.toml @@ -3,5 +3,4 @@ bind_port = "443" ssl_private_key_path = "./remote.com.key" ssl_cert_path = "./remote.com.crt" remote_host = "remote.com" -remote_port = "443" -verbose_level = "2" +remote_port = "443" \ No newline at end of file diff --git a/examples/modifydata/src/main.rs b/examples/modifydata/src/main.rs new file mode 100644 index 0000000..e19b231 --- /dev/null +++ b/examples/modifydata/src/main.rs @@ -0,0 +1,46 @@ +use sslrelay::{self, ConfigType, RelayConfig, HandlerCallbacks}; + +// Handler object +#[derive(Clone)] // Must have Clone trait implemented. +struct Handler; + +/* + Callback traits that can be used to read or inject/modify data + into upstream or downstream data. +*/ +impl HandlerCallbacks for Handler { + + // DownStream non blocking callback + fn ds_nb_callback(&self, _in_data: Vec) { + println!("[+] Data before complete rewrite:\n{:#04X?}", _in_data); + } + + // DownStream blocking callback + fn ds_b_callback(&self, _in_data: &mut Vec) { + _in_data.reverse(); + println!("[+] Data rewritten to:\n{:#04X?}", _in_data); + } + + // UpStream non blocking callback + fn us_nb_callback(&self, _in_data: Vec) { + println!("[+] Data before complete rewrite:\n{:#04X?}", _in_data); + } + + // UpStream blocking callback + fn us_b_callback(&self, _in_data: &mut Vec) { + _in_data.reverse(); + println!("[+] Data rewritten to:\n{:#04X?}", _in_data); + } +} + +fn main() { + + // Create new SSLRelay object + let mut relay = sslrelay::SSLRelay::new(Handler); + + // Load Configuration + relay.load_config(ConfigType::Default); + + // Start listening + relay.start(); +} \ No newline at end of file diff --git a/relay_config.example.toml b/relay_config.example.toml index 1003c17..bdc1e57 100644 --- a/relay_config.example.toml +++ b/relay_config.example.toml @@ -4,4 +4,3 @@ ssl_private_key_path = "./ssl.key" ssl_cert_path = "./ssl.crt" remote_host = "remote.com" remote_port = "443" -verbose_level = "2" \ No newline at end of file diff --git a/src/data.rs b/src/data.rs index f883438..9ea9bfe 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,141 +1,432 @@ use std::time::Duration; use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode}; -use std::net::TcpStream; use std::io::{self, Read, Write}; +use std::net::{TcpStream, Shutdown}; +use std::sync::mpsc::{self, Receiver, Sender}; +use std::thread; +use std::sync::{Arc, Mutex}; -#[derive(PartialEq)] -pub enum StreamDirection { - Upstream,// Data coming from remote host - DownStream,// Data coming from origin host +use crate::{HandlerCallbacks, InnerHandlers}; + +enum FullDuplexTcpState { + DownStreamRead, + DownStreamWrite(Vec), + UpStreamRead, + UpStreamWrite(Vec), + DownStreamShutDown, + UpStreamShutDown, } -pub struct DataHandler { - pub tcp_stream: Option>, - relay_stream: Option>, - remote_host: String, - pub stream_direction: StreamDirection, +enum DataPipe { + DataWrite(Vec), + Finished, + Shutdown, } -impl DataHandler { +struct DownStreamInner { + ds_stream: Option>>>, + internal_data_buffer: Vec, +} - pub fn new(tcp_stream: SslStream, remote_host: String) -> Self { - let _ = tcp_stream.get_ref().set_read_timeout(Some(Duration::from_millis(100))); - DataHandler { - tcp_stream: Some(tcp_stream), - relay_stream: None, - remote_host, - stream_direction: StreamDirection::DownStream, +impl DownStreamInner { + pub fn ds_handler(&mut self, data_out: Sender, data_in: Receiver) { + + loop { + + match data_in.recv_timeout(Duration::from_millis(100)) { + + // DataPipe Received + Ok(data_received) => { + + match data_received { + DataPipe::DataWrite(data) => { + + let mut stream_lock = match self.ds_stream.as_ref().unwrap().lock() { + Ok(sl) => sl, + Err(_e) => { + println!("[!] Failed to get stream lock!"); + return; + } + }; + + match stream_lock.write_all(&data) { + Ok(()) => {}, + Err(_e) => { + println!("[!] Failed to write data to DownStream tcp stream!"); + } + } + let _ = stream_lock.flush(); + drop(stream_lock); + + let _ = data_out.send(FullDuplexTcpState::DownStreamRead); + }, + DataPipe::Shutdown => { + let _ = self.ds_stream.as_ref().unwrap().lock().unwrap().get_ref().shutdown(Shutdown::Both); + return; + }, + _ => {} + } + }, + Err(_e) => { + match _e { + mpsc::RecvTimeoutError::Timeout => {}, + mpsc::RecvTimeoutError::Disconnected => { + println!("[!] DownStream data_in channel is disconnected!"); + } + } + } + }// End of data_in receive + + // If received data + if let Some(byte_count) = self.get_data_stream() { + if byte_count > 0 { + + let _ = data_out.send(FullDuplexTcpState::UpStreamWrite(self.internal_data_buffer.clone())); + if let DataPipe::Finished = data_in.recv().unwrap() { + self.internal_data_buffer.clear(); + continue; + } else { + println!("[!] Could not receive DataPipe::Finished notifier!"); + } + + } else if byte_count == 0 { + + let _ = data_out.send(FullDuplexTcpState::DownStreamShutDown); + let _ = self.ds_stream.as_ref().unwrap().lock().unwrap().get_ref().shutdown(Shutdown::Both); + return; + } else if byte_count == -1 { + continue; + } + } else { + + } } } - pub fn get_data_stream(&mut self, data: &mut Vec) -> usize { + fn get_data_stream(&mut self) -> Option { - let mut data_length: usize = 0; + let mut data_length: i64 = 0; - match self.stream_direction { + loop { - StreamDirection::DownStream => { + let mut r_buf = [0; 1024]; - loop { + match self.ds_stream.as_mut().unwrap().lock().unwrap().read(&mut r_buf) { - let mut r_buf = [0; 1024]; + Ok(bytes_read) => { - match self.tcp_stream.as_mut().unwrap().read(&mut r_buf) { + if bytes_read == 0 { + break; - Ok(bytes_read) => { + } else if bytes_read != 0 && bytes_read <= 1024 { - if bytes_read == 0 { - break; + let mut tmp_buf = r_buf.to_vec(); + tmp_buf.truncate(bytes_read); - } else if bytes_read != 0 && bytes_read <= 1024 { + let _bw = self.internal_data_buffer.write(&tmp_buf).unwrap(); + data_length += bytes_read as i64; - let mut tmp_buf = r_buf.to_vec(); - tmp_buf.truncate(bytes_read); + } else { + println!("[+] Else hit!!!!!!!!!!!!!!!!!!!!!!"); + } + }, + Err(e) => { + match e.kind() { - let _bw = data.write(&tmp_buf).unwrap(); - data_length += bytes_read; + io::ErrorKind::WouldBlock => { + if data_length == 0 { - } else { - println!("[+] Else hit!!!!!!!!!!!!!!!!!!!!!!"); + data_length = -1; } + break; + }, - Err(e) => { - match e.kind() { - io::ErrorKind::WouldBlock => { - break; - }, - _ => {println!("[!!!] Got error: {}",e);} + _ => {println!("[!!!] Got error: {}",e);} + } + }, + } + } + return Some(data_length); + } +} + +struct UpStreamInner{ + us_stream: Option>>>, + internal_data_buffer: Vec +} + +impl UpStreamInner { + pub fn us_handler(&mut self, data_out: Sender, data_in: Receiver) { + + loop { + + match data_in.recv_timeout(Duration::from_millis(100)) { + + Ok(data_received) => { + + match data_received { + DataPipe::DataWrite(data) => { + + let mut stream_lock = match self.us_stream.as_ref().unwrap().lock() { + Ok(sl) => sl, + Err(_e) => { + println!("[!] Failed to get stream lock!"); + return; + } + }; + + match stream_lock.write_all(&data) { + Ok(()) => {}, + Err(_e) => { + println!("[!] Failed to write data to DownStream tcp stream!"); + } } + let _ = stream_lock.flush(); + drop(stream_lock); + + let _ = data_out.send(FullDuplexTcpState::UpStreamRead); }, + DataPipe::Shutdown => { + let _ = self.us_stream.as_ref().unwrap().lock().unwrap().get_ref().shutdown(Shutdown::Both); + return; + } + _ => {} + } + }, + Err(e) => { + match e { + mpsc::RecvTimeoutError::Timeout => {}, + mpsc::RecvTimeoutError::Disconnected => { + println!("[!] UpStream data_in channel is disconnected!"); + } } } - }, - StreamDirection::Upstream => { - loop { + }// End of data_in receive - let mut r_buf = [0; 1024]; + if let Some(byte_count) = self.get_data_stream() { + if byte_count > 0 { - match self.relay_stream.as_mut().unwrap().read(&mut r_buf) { - - Ok(bytes_read) => { - - if bytes_read == 0 { - break; - - } else if bytes_read != 0 && bytes_read <= 1024 { - - let mut tmp_buf = r_buf.to_vec(); - tmp_buf.truncate(bytes_read); - - let _bw = data.write(&tmp_buf).unwrap(); - data_length += bytes_read; - - } else { - println!("[+] Else hit!!!!!!!!!!!!!!!!!!!!!!"); - } - }, - Err(e) => { - match e.kind() { - io::ErrorKind::WouldBlock => { - break; - }, - _ => {println!("[!!!] Got error: {}",e);} - } - }, + let _ = data_out.send(FullDuplexTcpState::DownStreamWrite(self.internal_data_buffer.clone())); + if let DataPipe::Finished = data_in.recv().unwrap() { + self.internal_data_buffer.clear(); + continue; + } else { + println!("[!] Could not receive DataPipe::Finished notifier!"); } + + } else if byte_count == 0 { + + let _ = data_out.send(FullDuplexTcpState::UpStreamShutDown); + let _ = self.us_stream.as_ref().unwrap().lock().unwrap().get_ref().shutdown(Shutdown::Both); + return; + } else if byte_count == -1 { + continue; + } + } else { + } + } + } + + fn get_data_stream(&mut self) -> Option { + + let mut data_length: i64 = 0; + + loop { + + let mut r_buf = [0; 1024]; + + match self.us_stream.as_mut().unwrap().lock().unwrap().read(&mut r_buf) { + + Ok(bytes_read) => { + + if bytes_read == 0 { + + break; + + } else if bytes_read != 0 && bytes_read <= 1024 { + + let mut tmp_buf = r_buf.to_vec(); + tmp_buf.truncate(bytes_read); + + let _bw = self.internal_data_buffer.write(&tmp_buf).unwrap(); + data_length += bytes_read as i64; + + } else { + println!("[+] Else hit!!!!!!!!!!!!!!!!!!!!!!"); + } + }, + Err(e) => { + match e.kind() { + + io::ErrorKind::WouldBlock => { + if data_length == 0 { + data_length = -1; + } + break; + }, + _ => {println!("[!!!] Got error: {}",e);} + } + }, + } + } + return Some(data_length); + } +} + +pub struct FullDuplexTcp +where + H: HandlerCallbacks + std::marker::Sync + std::marker::Send + Clone + 'static, +{ + ds_tcp_stream: Arc>>, + us_tcp_stream: Option>>>, + remote_endpoint: String, + ds_inner_m: Arc>, + us_inner_m: Arc>, + inner_handlers: InnerHandlers, +} + +impl FullDuplexTcp { + + pub fn new(ds_tcp_stream: SslStream, remote_endpoint: String, handlers: InnerHandlers) -> Self { + + let _ = ds_tcp_stream.get_ref().set_read_timeout(Some(Duration::from_millis(100))); + + FullDuplexTcp { + ds_tcp_stream: Arc::new(Mutex::new(ds_tcp_stream)), + us_tcp_stream: None, + remote_endpoint, + ds_inner_m: Arc::new(Mutex::new(DownStreamInner{ds_stream: None, internal_data_buffer: Vec::::new()})), + us_inner_m: Arc::new(Mutex::new(UpStreamInner{us_stream: None, internal_data_buffer: Vec::::new()})), + inner_handlers: handlers, + } + } + + pub fn handle(&mut self) { + + if self.connect_endpoint() == -1 { + let _ = self.ds_tcp_stream.lock().unwrap().get_ref().shutdown(Shutdown::Both); + return; + } + + let (state_sender, state_receiver): (Sender, Receiver) = mpsc::channel(); + let (ds_data_pipe_sender, ds_data_pipe_receiver): (Sender, Receiver) = mpsc::channel(); + let (us_data_pipe_sender, us_data_pipe_receiver): (Sender, Receiver) = mpsc::channel(); + + self.ds_inner_m.lock().unwrap().ds_stream = Some(self.ds_tcp_stream.clone()); + let ds_method_pointer = self.ds_inner_m.clone(); + let ds_state_bc = state_sender.clone(); + + self.us_inner_m.lock().unwrap().us_stream = Some(self.us_tcp_stream.as_ref().unwrap().clone()); + let us_method_pointer = self.us_inner_m.clone(); + let us_state_bc = state_sender.clone(); + + thread::spawn(move || { + ds_method_pointer.lock().unwrap().ds_handler(ds_state_bc, ds_data_pipe_receiver); + }); + + thread::spawn(move || { + us_method_pointer.lock().unwrap().us_handler(us_state_bc, us_data_pipe_receiver); + }); + + loop { + + match state_receiver.recv() { + Ok(state_request) => { + match state_request { + + // DownStream Write Request + FullDuplexTcpState::DownStreamWrite(mut data) => { + + /* + Callbacks that work with data from UpStream go here + */ + + let inner_handlers_clone = self.inner_handlers.clone(); + let in_data = data.clone(); + + thread::spawn(move || { + inner_handlers_clone.cb.us_nb_callback(in_data); + }); + + self.inner_handlers.cb.us_b_callback(&mut data); + let _ = ds_data_pipe_sender.send(DataPipe::DataWrite(data)); + + match state_receiver.recv().unwrap() { + FullDuplexTcpState::DownStreamRead => { + let _ = us_data_pipe_sender.send(DataPipe::Finished); + }, + _ => {} + } + }, + // UpStream Write Request + FullDuplexTcpState::UpStreamWrite(mut data) => { + + /* + Callbacks that work with data from DownStream go here + */ + + let inner_handlers_clone = self.inner_handlers.clone(); + let in_data = data.clone(); + + thread::spawn(move || { + inner_handlers_clone.cb.ds_nb_callback(in_data); + }); + + self.inner_handlers.cb.ds_b_callback(&mut data); + let _ = us_data_pipe_sender.send(DataPipe::DataWrite(data)); + + match state_receiver.recv().unwrap() { + FullDuplexTcpState::UpStreamRead => { + let _ = ds_data_pipe_sender.send(DataPipe::Finished); + }, + _ => {} + } + }, + // DownStreamShutDown Request + FullDuplexTcpState::DownStreamShutDown => { + let _ = us_data_pipe_sender.send(DataPipe::Shutdown); + return; + }, + // UpStreamShutDown Request + FullDuplexTcpState::UpStreamShutDown => { + let _ = ds_data_pipe_sender.send(DataPipe::Shutdown); + return; + }, + _ => {} + } + }, + Err(_e) => { + println!("[!] State receiver communication channel has closed!"); } } } - return data_length; } + + fn connect_endpoint(&mut self) -> i8 { - pub fn relay_data(&mut self, data: &Vec) -> Option { + let mut sslbuilder = SslConnector::builder(SslMethod::tls()).unwrap(); + sslbuilder.set_verify(SslVerifyMode::NONE); - let mut retries = 3; - loop { + let connector = sslbuilder.build(); - let mut sslbuild = SslConnector::builder(SslMethod::tls()).unwrap(); - sslbuild.set_verify(SslVerifyMode::NONE); - let connector = sslbuild.build(); - let stream = TcpStream::connect(&self.remote_host).unwrap(); - let _ = stream.set_read_timeout(Some(Duration::from_millis(500))); - let mut stream = match connector.connect(&self.remote_host, stream) { - Ok(s) => s, - Err(e) => { - println!("[Error] {}", e); - if retries == 0 { - println!("[!] Request relay retries: 0"); - return None; - } - retries -= 1; - continue; - } - }; + let s = match TcpStream::connect(self.remote_endpoint.as_str()) { + Ok(s) => s, + Err(e) => { + println!("[!] Can't connect to remote host: {}\nErr: {}", self.remote_endpoint, e); + return -1; + } + }; - stream.write_all(&data).unwrap(); - let _ = stream.flush(); - self.relay_stream = Some(stream); - return Some(0); - } + let r_host: Vec<&str> = self.remote_endpoint.as_str().split(":").collect(); + + let s = connector.connect(r_host[0], s).unwrap(); + + self.us_tcp_stream = Some( + Arc::new( + Mutex::new( + s + ))); + let _ = self.us_tcp_stream.as_ref().unwrap().lock().unwrap().get_ref().set_read_timeout(Some(Duration::from_millis(100))); + return 0; } -} // DataHandler \ No newline at end of file +} \ No newline at end of file diff --git a/src/http.rs b/src/http.rs deleted file mode 100644 index 3565213..0000000 --- a/src/http.rs +++ /dev/null @@ -1,312 +0,0 @@ -use httparse::{self, Header}; -use chunked_transfer::Decoder; -use flate2::read::GzDecoder; -use std::thread; -use std::time::Duration; -use std::io::Read; - -pub struct RelayedResponse<'a> { - http_version: Option, - http_code: Option, - http_reason: Option, - http_headers: Option>>, - http_body: Option, -} - -pub struct RelayRequest<'a> { - http_method: Option, - http_path: Option, - http_version: Option, - http_headers: Option>>, - http_body: Option, -} - -/* Unused HTTP helper functions -pub fn get_req_headers<'a>(data: &'a Vec) -> Option>> { - - let mut headers = [httparse::EMPTY_HEADER; 128]; - let mut request = httparse::Request::new(&mut headers); - - let req = request.parse(data).unwrap(); - - if req.is_complete() { - return Some(headers.to_vec()); - } - None -} - -pub fn get_res_headers(data: &Vec) -> Option> { - - let mut headers = [httparse::EMPTY_HEADER; 128]; - let mut response = httparse::Response::new(&mut headers); - - let res = response.parse(data).unwrap(); - - if res.is_complete() { - return Some(headers.to_vec()); - } - None -} - -pub fn get_host(data: &Vec) -> Option { - - return Some("137.220.37.67".to_string()); - let mut headers = [httparse::EMPTY_HEADER; 128]; - - let mut request = httparse::Request::new(&mut headers); - - let req = request.parse(data).unwrap(); - - if req.is_complete() { - for header in headers.iter() { - if header.name == "Host" || header.name == "host" { - //println!("Host -> {}", String::from_utf8(header.value.to_vec()).unwrap()); - return Some(String::from_utf8(header.value.to_vec()).unwrap()); - } - } - None - } else if req.is_partial() { - for header in headers.iter() { - if header.name == "Host" || header.name == "host" { - //println!("Host -> {}", String::from_utf8(header.value.to_vec()).unwrap()); - return Some(String::from_utf8(header.value.to_vec()).unwrap()); - } - } - None - } else { - None - } -} - -pub fn get_cookie(data: &Vec) -> Option { - - let mut headers = [httparse::EMPTY_HEADER; 128]; - - let mut request = httparse::Request::new(&mut headers); - - let req = request.parse(data).unwrap(); - - if req.is_complete() { - for header in headers.iter() { - if header.name == "Cookie" || header.name == "cookie" { - //println!("Host -> {}", String::from_utf8(header.value.to_vec()).unwrap()); - return Some(String::from_utf8(header.value.to_vec()).unwrap()); - } - } - None - } else if req.is_partial() { - for header in headers.iter() { - if header.name == "Cookie" || header.name == "cookie" { - //println!("Host -> {}", String::from_utf8(header.value.to_vec()).unwrap()); - return Some(String::from_utf8(header.value.to_vec()).unwrap()); - } - } - None - } else { - None - } -} -*/ -pub fn http_req_verbose(data: &Vec, mode: u8) { - - let req_info = get_request(&data); - let req_header_list = req_info.http_headers.unwrap(); - let mut req_header_string = String::new(); - - for header in req_header_list { - if header.value.to_vec().len() == 0 {continue;} - req_header_string.push_str(format!("[-->] {}: {}\n", header.name, String::from_utf8(header.value.to_vec()).unwrap()).as_str()); - } - if mode == 1 { - println!("================================\n[-->] HTTP Version: {}\n[-->] HTTP Method: {} {}\n[-->] HTTP Headers:\n{}\n[-->] HTTP Body:\n{}\n" - ,req_info.http_version.unwrap() - ,req_info.http_method.unwrap() - ,req_info.http_path.unwrap() - ,req_header_string - ,req_info.http_body.unwrap() - ); - } else if mode == 2 { - println!("[Req] {} {}", req_info.http_method.unwrap(), req_info.http_path.unwrap()); - } -} - -pub fn http_res_verbose(response_data: &Vec, mode: u8) { - - let res_info = get_response(&response_data); - let header_list = res_info.http_headers.unwrap(); - let mut header_string = String::new(); - - for header in header_list { - if header.value.to_vec().len() == 0 {continue;} - header_string.push_str(format!("[<--] {}: {}\n", header.name, String::from_utf8(header.value.to_vec()).unwrap()).as_str()); - } - if mode == 1 { - println!("\n[<--] HTTP Version: {}\n[<--] HTTP Code: {} {}\n[<--] HTTP Headers:\n{}\n[<--] HTTP Body:\n{}\n================================\n" - ,res_info.http_version.unwrap() - ,res_info.http_code.unwrap() - ,res_info.http_reason.unwrap() - ,header_string - ,res_info.http_body.unwrap() - ); - } else if mode == 2 { - println!("[Res] {} {}", res_info.http_code.unwrap(), res_info.http_reason.unwrap()); - } -} - -pub fn get_response<'a>(data: &'a Vec) -> RelayedResponse<'a> { - - let mut headers = [httparse::EMPTY_HEADER; 128]; - let mut res = httparse::Response::new(&mut headers); - - let res_chk = res.parse(data); - - loop { - - if !res_chk.unwrap().is_complete() { - thread::sleep(Duration::from_millis(100)); - continue; - } - - let reason = match res.reason { - Some(r) => Some(r.to_string()), - None => None, - }; - - let header_vec = res.headers.to_vec(); - - let body = get_http_body(&data, header_vec).unwrap_or(String::from("[-] Failed to get http response body!").into_bytes()); - let string_body = String::from_utf8(body).unwrap(); - - return RelayedResponse { - http_version: res.version, - http_code: res.code, - http_reason: reason, - http_headers: Some(headers.to_vec()), - http_body: Some(string_body), - }; - } -} - -pub fn get_request<'a>(data: &'a Vec) -> RelayRequest<'a> { - - let mut headers = [httparse::EMPTY_HEADER; 128]; - let mut req = httparse::Request::new(&mut headers); - - let req_chk = req.parse(data); - - loop { - - if !req_chk.unwrap().is_complete() { - thread::sleep(Duration::from_millis(100)); - continue; - } - - let method = match req.method { - Some(r) => Some(r.to_string()), - None => None, - }; - - let path = match req.path { - Some(p) => Some(p.to_string()), - None => None, - }; - - let header_vec = req.headers.to_vec(); - - let body = get_http_body(&data, header_vec).unwrap_or(String::from("[-] Failed to get http request body!").into_bytes()); - let string_body = String::from_utf8(body).unwrap(); - - return RelayRequest { - http_method: method, - http_path: path, - http_version: req.version, - http_headers: Some(headers.to_vec()), - http_body: Some(string_body), - }; - } -} - -pub fn get_http_body(data: &Vec, headers: Vec
) -> Option> { - - let mut cl: u64 = 0; - let encoding_check = headers.clone(); - for header in headers { - - if header.name == "Content-Length" || header.name == "content-length" { - let length = match String::from_utf8(header.value.to_vec()) { - Ok(s) => s, - Err(e) => { - println!("[!] ERROR: {}",e); - return None; - }, - }; - - cl += length.parse::().unwrap(); - let mut r_body = data.to_vec(); - r_body.reverse(); - r_body.truncate(cl as usize); - r_body.reverse(); - - return Some(r_body); - - } else if header.name == "Transfer-Encoding" || header.name == "transfer-encoding" { - - let te_string = String::from_utf8(header.value.to_vec()).unwrap(); - - if te_string == "chunked" { - - let mut new_vec = data.to_vec(); - let new_vec_iter = new_vec.to_vec(); - let mut new_vec_iter = new_vec_iter.iter(); - let mut i = 0; - - loop { - if let Some(&0x0d) = new_vec_iter.next() { - i += 1; - if let Some(&0x0a) = new_vec_iter.next() { - i += 1; - if let Some(&0x0d) = new_vec_iter.next() { - i += 1; - if let Some(&0x0a) = new_vec_iter.next() { - i += 1; - break; - } else {i += 1;} - } else {i += 1;} - } else {i += 1;} - } else {i += 1;} - } - - new_vec.reverse(); - new_vec.truncate(data.len() - i); - new_vec.reverse(); - - let mut decoder = Decoder::new(new_vec.as_slice()); - let mut blob = Vec::new(); - let _ = decoder.read_to_end(&mut blob); - - let mut g_encoded = false; - for header in encoding_check { - if header.name == "Content-Encoding" || header.name == "content-encoding" { - let s = String::from_utf8(header.value.to_vec()).unwrap(); - if s == "gzip" { - g_encoded = true; - } - } - } - if g_encoded { - let mut gzd = GzDecoder::new(&blob[..]); - let mut unzipped = Vec::new(); - match gzd.read_to_end(&mut unzipped) { - Ok(_) => {}, - Err(_) => return None, - } - - return Some(unzipped); - } else { - return Some(blob); - } - } - } - } - return Some(String::from("None").into_bytes()); -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0354727..62e71bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ -use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream}; -use std::io::Write; -use std::net::{TcpListener, TcpStream}; +use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; +use std::net::{TcpListener}; use std::sync::{Arc, Mutex}; use std::{process, thread}; use std::env; @@ -10,10 +9,7 @@ use std::path::Path; use toml::Value as TValue; mod data; -use data::{DataHandler, StreamDirection}; - -mod http; -use http as http_helper; +use data::FullDuplexTcp; #[derive(Clone)] pub struct RelayConfig { @@ -23,7 +19,6 @@ pub struct RelayConfig { pub remote_port: String, pub ssl_private_key_path: String, pub ssl_cert_path: String, - pub verbose_level: i8, } pub trait HandlerCallbacks { @@ -43,19 +38,27 @@ pub enum ConfigType { #[derive(Clone)] pub struct SSLRelay where - H: HandlerCallbacks + std::marker::Sync + std::marker::Send + 'static, + H: HandlerCallbacks + std::marker::Sync + std::marker::Send + Clone + 'static, { config: Option, - handlers: Option, + handlers: Option>, } -impl SSLRelay { +#[derive(Clone)] +pub struct InnerHandlers +where + H: HandlerCallbacks + std::marker::Sync + std::marker::Send + Clone + 'static, +{ + cb: H +} + +impl SSLRelay { pub fn new(handlers: H) -> Self { SSLRelay { config: None, - handlers: Some(handlers), + handlers: Some(InnerHandlers{cb: handlers}), } } @@ -66,7 +69,11 @@ impl SSLR pub fn start(&mut self) { let rc_pointer = Arc::new(Mutex::new(self.config.as_ref().unwrap().clone())); - let handler_pointer = Arc::new(Mutex::new(self.handlers.take().unwrap())); + + let rhost = rc_pointer.lock().unwrap().remote_host.clone(); + let rport = rc_pointer.lock().unwrap().remote_port.clone(); + let remote_endpoint = format!("{}:{}", rhost, rport); + let acceptor = self.setup_ssl_config(self.config.as_ref().unwrap().ssl_private_key_path.clone(), self.config.as_ref().unwrap().ssl_cert_path.clone()); let listener = TcpListener::bind(format!("{}:{}", self.config.as_ref().unwrap().bind_host.clone(), self.config.as_ref().unwrap().bind_port.clone())).unwrap(); @@ -76,26 +83,23 @@ impl SSLR Ok(stream) => { let acceptor = acceptor.clone(); - let rc_config = rc_pointer.clone(); - let handler = handler_pointer.clone(); + //let rc_config = rc_pointer.clone(); + let handler_clone = self.handlers.as_ref().unwrap().clone(); + let r_endpoint = remote_endpoint.clone(); thread::spawn(move || { match acceptor.accept(stream) { Ok(stream) => { - - handle_stream(stream, rc_config, handler); - return 0; + // FULL DUPLEX OBJECT CREATION HERE + FullDuplexTcp::new(stream, r_endpoint, handler_clone).handle(); }, Err(e) => { println!("[Error] {}", e); - return -1; } } }); - /*let stream = acceptor.accept(stream).unwrap(); - handle_stream(stream, rc_config, handler);*/ }, Err(e) => {println!("[Error] Tcp Connection Failed: {}", e)} } @@ -134,7 +138,6 @@ impl SSLR let ssl_cert_path = config_parsed["ssl_cert_path"].to_string().replace("\"", ""); let remote_host = config_parsed["remote_host"].to_string().replace("\"", ""); let remote_port = config_parsed["remote_port"].to_string().replace("\"", ""); - let verbose_level = config_parsed["verbose_level"].to_string().replace("\"", "").parse().unwrap(); RelayConfig { bind_host: bind_host.clone(), @@ -143,7 +146,6 @@ impl SSLR ssl_cert_path: ssl_cert_path.clone(), remote_host: remote_host.clone(), remote_port: remote_port.clone(), - verbose_level: verbose_level, } } @@ -163,81 +165,4 @@ impl SSLR acceptor.check_private_key().unwrap(); Arc::new(acceptor.build()) } -}// SSLRelay - -/* Rewrite this to handle TCP connections until TCP connection is dropped instead of dropping it */ -fn handle_stream(tcp_stream: SslStream, rc_config: Arc>, handlers: Arc>) { - - let conf_lock = rc_config.lock().unwrap(); - let remote_host = format!("{}:{}", conf_lock.remote_host, conf_lock.remote_port); - let verbose_mode = conf_lock.verbose_level; - drop(conf_lock); - - let mut datahandler = DataHandler::new(tcp_stream, remote_host); - - let mut data = Vec::::new(); - let mut response_data = Vec::::new(); - - let data_size = datahandler.get_data_stream(&mut data); - if data_size == 0 { - println!("[!] Got 0 bytes closing tcp stream!"); - return; - } - if verbose_mode == 1 { - http_helper::http_req_verbose(&data, 1); - } else if verbose_mode == 2 { - http_helper::http_req_verbose(&data, 2); - } - - let handlers_p = handlers.clone(); - let d = data.clone(); - - thread::spawn(move || { - let handlers_lock = handlers_p.lock().unwrap(); - handlers_lock.ds_nb_callback(d); - drop(handlers_lock); - }); - - let handlers_p = handlers.clone(); - let handlers_lock = handlers_p.lock().unwrap(); - handlers_lock.ds_b_callback(&mut data); - drop(handlers_lock); - - match datahandler.relay_data(&data) { - Some(_relay_success) => {}, - None => { - println!("[-] relay_data failed!"); - return; - } - } - - // Get Upstream Data - datahandler.stream_direction = StreamDirection::Upstream; - let _response_size = datahandler.get_data_stream(&mut response_data); - - if verbose_mode == 1 { - http_helper::http_res_verbose(&response_data, 1); - } else if verbose_mode == 2 { - http_helper::http_res_verbose(&response_data, 2); - } - - // Switch back to DownStream mode to relay data from remote host back to origin host - datahandler.stream_direction = StreamDirection::DownStream; - - let handlers_p = handlers.clone(); - let d = response_data.clone(); - - thread::spawn(move || { - let handlers_lock = handlers_p.lock().unwrap(); - handlers_lock.us_nb_callback(d); - drop(handlers_lock); - }); - - let handlers_p = handlers.clone(); - let handlers_lock = handlers_p.lock().unwrap(); - handlers_lock.us_b_callback(&mut response_data); - drop(handlers_lock); - - datahandler.tcp_stream.as_mut().unwrap().write_all(&response_data).unwrap(); - let _ = datahandler.tcp_stream.as_mut().unwrap().flush(); -} \ No newline at end of file +}// SSLRelay \ No newline at end of file