feat: use streams for tls

This commit is contained in:
Mahdi Dibaiee 2021-03-16 15:21:02 +00:00
parent be3f8f4cff
commit c3f3321a57
7 changed files with 722 additions and 109 deletions

369
Cargo.lock generated
View File

@ -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"

View File

@ -1,5 +1,5 @@
[package]
name = "tls-forward-proxy"
name = "tlsproxy"
version = "0.1.0"
authors = ["Mahdi Dibaiee <mdibaiee@pm.me>"]
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"

16
src/cert.rs Normal file
View File

@ -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<ServerCertVerified, TLSError> {
Ok(ServerCertVerified::assertion())
}
}

107
src/command.rs Normal file
View File

@ -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<String>,
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 <mdibaiee@pm.me>")
.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::<u16>()
.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<rustls::Certificate> {
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<fs::File> {
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
);
}

View File

@ -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<u8>) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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<dyn std::error::Error + Send + Sync>> {
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);

73
src/proxy.rs Normal file
View File

@ -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<dyn Error>> {
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(())
}

117
src/proxy/ops.rs Normal file
View File

@ -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<u8>) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<T: Read>(stream: &mut T, buf: &mut Vec<u8>) -> Result<usize, Box<dyn Error>> {
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<T: Write>(stream: &mut T, buf: &[u8]) -> Result<usize, Box<dyn Error>> {
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<T: Session, R: Read>(stream: &mut T, buf: &mut R) -> Result<usize, Box<dyn Error>> {
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<T: Session, W: Write>(stream: &mut T, buf: &mut W) -> Result<usize, Box<dyn Error>> {
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());
}
}
}
}