diff --git a/Cargo.lock b/Cargo.lock index c94d729..05f1450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,29 +1,174 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + [[package]] name = "bytes" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "futures" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" + +[[package]] +name = "futures-executor" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" + +[[package]] +name = "futures-macro" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" + +[[package]] +name = "futures-task" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" + +[[package]] +name = "futures-util" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + [[package]] name = "hermit-abi" version = "0.1.18" @@ -48,6 +193,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "js-sys" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.88" @@ -157,6 +317,24 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -184,12 +362,59 @@ dependencies = [ "bitflags", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09700171bbcc799d113f2c675314d6005c3dc035f3e7307cf3e7fd459ccbe246" +dependencies = [ + "base64", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "signal-hook-registry" version = "1.3.0" @@ -199,6 +424,12 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + [[package]] name = "smallvec" version = "1.6.1" @@ -216,6 +447,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "syn" version = "1.0.64" @@ -228,17 +471,33 @@ dependencies = [ ] [[package]] -name = "tls-forward-proxy" +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tlsproxy" version = "0.1.0" dependencies = [ + "clap", + "futures", "httparse", + "rustls", + "rustls-pemfile", "tokio", + "webpki", + "webpki-roots", ] [[package]] name = "tokio" -version = "1.2.0" -source = "git+https://github.com/conblem/tokio/?branch=bidi_copy#6fe69cd16aa24623697f5f44ce4c6fabe85245b2" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d56477f6ed99e10225f38f9f75f872f29b8b8bd8c0b946f63345bb144e9eeda" dependencies = [ "autocfg", "bytes", @@ -257,19 +516,121 @@ dependencies = [ [[package]] name = "tokio-macros" version = "1.1.0" -source = "git+https://github.com/conblem/tokio?branch=bidi_copy#6fe69cd16aa24623697f5f44ce4c6fabe85245b2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "wasm-bindgen" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" + +[[package]] +name = "web-sys" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +dependencies = [ + "webpki", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 8c791a5..e4c49e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tls-forward-proxy" +name = "tlsproxy" version = "0.1.0" authors = ["Mahdi Dibaiee "] edition = "2018" @@ -7,6 +7,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -# tokio = { version = "1", features = ["full"] } -tokio = { git = "https://github.com/conblem/tokio", branch = "bidi_copy", features = ["full"] } +tokio = { version = "1", features = ["full"] } httparse = "1.3.4" +futures = "0.3.13" +rustls = { version = "0.19.0", features = ["dangerous_configuration"] } +webpki = "0.21.4" +webpki-roots = "0.21.0" +rustls-pemfile = "0.2.0" +clap = "2.33.3" diff --git a/src/cert.rs b/src/cert.rs new file mode 100644 index 0000000..d7efc01 --- /dev/null +++ b/src/cert.rs @@ -0,0 +1,16 @@ +use rustls::{Certificate, RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError}; +use webpki; + +pub struct NoCertificateVerification {} + +impl ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _roots: &RootCertStore, + _presented_certs: &[Certificate], + _dns_name: webpki::DNSNameRef<'_>, + _ocsp_response: &[u8], + ) -> Result { + Ok(ServerCertVerified::assertion()) + } +} diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..8dbf110 --- /dev/null +++ b/src/command.rs @@ -0,0 +1,107 @@ +use rustls_pemfile; +use std::io::BufReader; +use std::{fs, str}; + +use clap::{App, Arg}; + +#[derive(Debug, Clone)] +pub struct Args { + pub port: u16, + pub certs: String, + pub key: String, + pub replaces: Vec, + pub verbose: bool, +} + +pub fn args() -> Args { + let version = env!("CARGO_PKG_NAME").to_string() + ", version: " + env!("CARGO_PKG_VERSION"); + + let matches = App::new("tlsproxy") + .version(&*version) + .author("Mahdi Dibaiee ") + .about("A simple TLS forward proxy capable of replacing parts of the outgoing traffic") + .arg(Arg::with_name("port") + .short("p") + .long("port") + .value_name("PORT") + .help("Listen on PORT [default: 8080]") + .takes_value(true)) + .arg(Arg::with_name("certs") + .long("certs") + .value_name("FILE") + .help("Read server certificates from FILE. This should contain PEM-format certificates in the right order. The first certificate should certify KEYFILE, the last should be a root CA.") + .required(true) + .takes_value(true)) + .arg(Arg::with_name("key") + .long("key") + .value_name("FILE") + .help("Read private key from FILE. This should be a RSA private key or PKCS8-encoded private key, in PEM format.") + .required(true) + .takes_value(true)) + .arg(Arg::with_name("replace") + .long("replace") + .value_name("PATTERN") + .help("Replace data in outgoing requests according to patterns specified in the s/MATCH/REPLACEMENT format.") + .multiple(true) + .use_delimiter(true) + .takes_value(true)) + .arg(Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Be noisy")) + .get_matches(); + + return Args { + port: matches + .value_of("port") + .unwrap_or("8080") + .parse::() + .unwrap(), + certs: matches + .value_of("certs") + .map(|a| a.to_owned()) + .expect("--certs must be specified"), + key: matches + .value_of("key") + .map(|a| a.to_owned()) + .expect("--key must be specified"), + verbose: matches.is_present("verbose"), + replaces: matches + .values_of("replace") + .map_or(vec![], |a| a.map(|b| b.to_owned()).collect()), + }; +} + +pub fn load_certs(filename: &str) -> Vec { + let certfile = fs::File::open(filename).expect("cannot open certificate file"); + let mut reader = BufReader::new(certfile); + rustls_pemfile::certs(&mut reader) + .unwrap() + .iter() + .map(|v| rustls::Certificate(v.clone())) + .collect() +} + +pub fn read_file(filename: &str) -> BufReader { + let certfile = fs::File::open(filename).expect("cannot open certificate file"); + BufReader::new(certfile) +} + +pub fn load_private_key(filename: &str) -> rustls::PrivateKey { + let keyfile = fs::File::open(filename).expect("cannot open private key file"); + let mut reader = BufReader::new(keyfile); + + loop { + match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") { + Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key), + Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key), + None => break, + _ => {} + } + } + + panic!( + "no keys found in {:?} (encrypted keys not supported)", + filename + ); +} diff --git a/src/main.rs b/src/main.rs index 63accb0..66591b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,117 +1,51 @@ +use std::sync::Arc; use std::net::SocketAddr; -use tokio::net::{TcpListener,TcpStream}; -use tokio::io::{copy_bidirectional}; -use std::error::Error; -use std::{str, io}; -use httparse::Request; +use tokio::net::{TcpListener}; +use rustls::{ServerConfig, ClientConfig, NoClientAuth}; +use webpki_roots; -async fn read(stream: &TcpStream, buf: &mut Vec) -> Result<(), Box> { - loop { - // Wait for the socket to be readable - stream.readable().await?; - - // Try to read data, this may still fail with `WouldBlock` - // if the readiness event is a false positive. - match stream.try_read(buf) { - Ok(n) => { - buf.truncate(n); - break; - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - continue; - } - Err(e) => { - println!("error reading from stream: {:#?}", e); - return Err(e.into()); - } - } - } - - Ok(()) -} - -async fn write(stream: &TcpStream, buf: &[u8]) -> Result<(), Box> { - loop { - // Wait for the socket to be writable - stream.writable().await?; - - // Try to write data, this may still fail with `WouldBlock` - // if the readiness event is a false positive. - match stream.try_write(buf) { - Ok(_) => { - break; - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - continue; - } - Err(e) => { - println!("error writing to stream: {:#?}", e); - return Err(e.into()); - } - } - } - - Ok(()) -} - -async fn proxy(mut incoming: TcpStream) -> Result<(), Box> { - let mut buf = vec![0; 1024]; - read(&incoming, &mut buf).await.unwrap(); - - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut req = Request::new(&mut headers); - req.parse(&buf).unwrap(); - - println!("{}\r\n------", str::from_utf8(&buf)?); - - match (req.method, req.path) { - (Some("CONNECT"), Some(ref path)) => { - println!("CONNECT {:#?}", path); - let mut outgoing = TcpStream::connect(path).await?; - - write(&incoming, b"HTTP/1.1 200 OK\r\n\r\n").await?; - - copy_bidirectional(&mut incoming, &mut outgoing).await?; - - /* A naive implementation of a bidirectional copy: - * I am actually concerned about this implementation since both reads happen at the - * same time, if in any case two streams are writing at the same time, some of the data - * of one of those writes might get discarded. - * - * let mut tmp = vec![0; 1024]; - * let mut tmp2 = vec![0; 1024]; - * loop { - * let r = select! { - * _ = read(&mut incoming, &mut tmp).fuse() => - * write(&mut outgoing, &tmp).fuse(), - * _ = read(&mut outgoing, &mut tmp2).fuse() => - * write(&mut incoming, &tmp2).fuse() - * }; - - * r.await? - * } - */ - } - - _ => { - println!("Not What I Expected!!"); - } - } - - return Ok(()) -} +mod command; +mod proxy; +mod cert; #[tokio::main] async fn main() -> Result<(), Box> { + let args = command::args(); + // We'll bind to 127.0.0.1:3000 - let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); + let addr = SocketAddr::from(([127, 0, 0, 1], args.port)); println!("Listening on {:#?}", addr); + let mut server_config = ServerConfig::new(NoClientAuth::new()); + let certs = command::load_certs(&args.certs); + let privkey = command::load_private_key(&args.key); + server_config + .set_single_cert(certs.clone(), privkey.clone()) + .expect("bad certificates/private key"); + + let mut client_config = ClientConfig::new(); + client_config.key_log = Arc::new(rustls::KeyLogFile::new()); + + client_config + .dangerous() + .set_certificate_verifier(Arc::new(cert::NoCertificateVerification {})); + + client_config + .root_store + .add_pem_file(&mut command::read_file(&args.certs)).unwrap(); + + //client_config + //.root_store + //.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + let tcp_listener = TcpListener::bind(addr).await?; loop { let (tcp_stream, _) = tcp_listener.accept().await?; + let cc = client_config.clone(); + let sc = server_config.clone(); + let args_copy = args.clone(); tokio::task::spawn(async move { - match proxy(tcp_stream).await { + match proxy::proxy(tcp_stream, cc, sc, args_copy).await { Ok(()) => {} Err(e) => { println!("error: {:#?}", e); diff --git a/src/proxy.rs b/src/proxy.rs new file mode 100644 index 0000000..9562b95 --- /dev/null +++ b/src/proxy.rs @@ -0,0 +1,73 @@ +use std::sync::Arc; +use tokio::net::{TcpStream}; +use std::error::Error; +use httparse::Request; +use webpki; +use std::str; +use std::io::{Write, Read}; +use rustls::{ServerConfig, ClientConfig, Session}; + +mod ops; + +pub async fn proxy(mut incoming: TcpStream, client_config: ClientConfig, server_config: ServerConfig, args: super::command::Args) -> Result<(), Box> { + let mut buf = vec![0; 1024]; + ops::read(&incoming, &mut buf).await.unwrap(); + + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut req = Request::new(&mut headers); + req.parse(&buf).unwrap(); + + if args.verbose { + println!("{}\r\n------", str::from_utf8(&buf)?); + } + + match (req.method, req.path) { + (Some("CONNECT"), Some(ref path)) => { + ops::write(&mut incoming, b"HTTP/1.1 200 OK\r\n\r\n").await?; + let host = path.split(":").next().expect("Hostname must be valid"); + let dns_name = webpki::DNSNameRef::try_from_ascii_str(host).unwrap(); + println!("{:#?}", dns_name); + + let mut server_session = rustls::ServerSession::new(&Arc::new(server_config)); + let mut incoming_std = incoming.into_std()?; + let mut incoming_tls = rustls::Stream::new(&mut server_session, &mut incoming_std); + + let mut client_session = rustls::ClientSession::new(&Arc::new(client_config), dns_name); + let outgoing = TcpStream::connect(path).await?; + let mut outgoing_std = outgoing.into_std()?; + let mut outgoing_tls = rustls::Stream::new(&mut client_session, &mut outgoing_std); + + let mut tmp = vec![0; 1024^2]; + let mut tmp2 = vec![0; 1024^2]; + //let ciphersuite = incoming_tls.sess.get_negotiated_ciphersuite().unwrap(); + //println!("{:#?}", ciphersuite); + loop { + println!("incoming"); + let incoming_read = ops::sync_read(&mut incoming_tls, &mut tmp); + println!("incoming {:#?}", str::from_utf8(&tmp)?); + match incoming_read { + Ok(n) if n > 0 => ops::sync_write(&mut outgoing_tls, &tmp), + Ok(0) => return Ok(()), + e => e + }.unwrap(); + + let outgoing_read = ops::sync_read(&mut outgoing_tls, &mut tmp2); + + println!("outgoing {:#?}", str::from_utf8(&tmp2)?); + + match outgoing_read { + Ok(n) if n > 0 => ops::sync_write(&mut incoming_tls, &tmp2), + Ok(0) => return Ok(()), + + e => e + }.unwrap(); + } + } + + _ => { + println!("Not What I Expected!!"); + } + } + + return Ok(()) +} diff --git a/src/proxy/ops.rs b/src/proxy/ops.rs new file mode 100644 index 0000000..a2a6527 --- /dev/null +++ b/src/proxy/ops.rs @@ -0,0 +1,117 @@ +use tokio::net::{TcpStream}; +use std::error::Error; +use std::{io}; +use std::io::{Read, Write}; +use rustls::{Session}; + +pub async fn read(stream: &TcpStream, buf: &mut Vec) -> Result<(), Box> { + loop { + stream.readable().await?; + + match stream.try_read(buf) { + Ok(n) => { + buf.truncate(n); + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error reading from stream: {:#?}", e); + return Err(e.into()); + } + } + } + + Ok(()) +} + +pub async fn write(stream: &TcpStream, buf: &[u8]) -> Result<(), Box> { + loop { + stream.writable().await?; + + match stream.try_write(buf) { + Ok(_) => { + break; + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error writing to stream: {:#?}", e); + return Err(e.into()); + } + } + } + + Ok(()) +} + +pub fn sync_read(stream: &mut T, buf: &mut Vec) -> Result> { + loop { + match stream.read(buf) { + Ok(n) => { + buf.truncate(n); + return Ok(n); + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error reading from stream: {:#?}", e); + return Err(e.into()); + } + } + } +} + +pub fn sync_write(stream: &mut T, buf: &[u8]) -> Result> { + loop { + match stream.write(buf) { + Ok(n) => { + return Ok(n); + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error writing to stream: {:#?}", e); + return Err(e.into()); + } + } + } +} + +pub fn tls_read(stream: &mut T, buf: &mut R) -> Result> { + loop { + match stream.read_tls(buf) { + Ok(n) => { + return Ok(n); + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error reading from stream: {:#?}", e); + return Err(e.into()); + } + } + } +} + +pub fn tls_write(stream: &mut T, buf: &mut W) -> Result> { + loop { + match stream.write_tls(buf) { + Ok(n) => { + return Ok(n); + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + println!("error writing to stream: {:#?}", e); + return Err(e.into()); + } + } + } +}