diff --git a/Cargo.lock b/Cargo.lock index d758f87..f89d8f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,25 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "bcrypt" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f055c8591efe08e03f534ee632672ea3643c3932e485f422107ec6cc1157b0ea" +dependencies = [ + "base64", + "blowfish", + "byteorder", + "lazy_static", + "rand", +] + [[package]] name = "bitflags" version = "0.5.0" @@ -99,10 +118,31 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug", +] + [[package]] name = "bubble-flexrouter" version = "0.1.0" dependencies = [ + "bcrypt", "clap", "futures", "futures-channel", @@ -113,12 +153,19 @@ dependencies = [ "hyper-tls", "lru", "pnet", + "rand", "tokio", "tower", "trust-dns-resolver", "whoami", ] +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + [[package]] name = "bytes" version = "0.5.6" @@ -312,6 +359,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -720,6 +776,12 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "openssl" version = "0.10.30" @@ -1511,6 +1573,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-bidi" version = "0.3.4" diff --git a/Cargo.toml b/Cargo.toml index a4b65ab..a4b982e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Jonathan Cobb "] edition = "2018" [dependencies] +bcrypt = "0.6.3" clap = "2.33.0" futures = "0.3.5" futures-core = { version = "0.3", default-features = false } @@ -16,6 +17,7 @@ hyper = { version = "0.13.7", features = ["stream"] } hyper-tls = "0.4.3" lru = "0.6.0" pnet = "0.26.0" +rand = "0.7.3" tokio = { version = "0.2.22", features = ["full"] } tower = "0.3.1" trust-dns-resolver = "0.19.5" diff --git a/src/main.rs b/src/main.rs index 1c6e9d9..c6c1408 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,32 +6,16 @@ extern crate lru; -use std::convert::Infallible; -use std::net::SocketAddr; use std::process::exit; -use std::sync::Arc; use clap::{Arg, ArgMatches, App}; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Client, Server}; -use hyper::client::HttpConnector; -use hyper_tls::HttpsConnector; - -use lru::LruCache; - use pnet::datalink; -use tokio::sync::Mutex; - use whoami; use bubble_flexrouter::pass::init_password; -use bubble_flexrouter::dns_cache::*; -use bubble_flexrouter::net::*; -use bubble_flexrouter::proxy::*; - -type HttpClient = Client>, hyper::Body>; +use bubble_flexrouter::proxy::start_proxy; // To try this example: // 1. cargo run --example http_proxy @@ -134,7 +118,7 @@ async fn main() { let password_file = password_file_opt.unwrap(); let password_opt = args.value_of("password_env_var"); - //let password = init_password(password_file, password_opt); + let password = init_password(password_file, password_opt); let proxy_ip_opt = args.value_of("proxy_ip"); if proxy_ip_opt.is_none() { @@ -160,44 +144,7 @@ async fn main() { } let dns1_ip = args.value_of("dns1").unwrap(); - let dns1_sock : SocketAddr = format!("{}:53", dns1_ip).parse().unwrap(); let dns2_ip = args.value_of("dns2").unwrap(); - let dns2_sock : SocketAddr = format!("{}:53", dns2_ip).parse().unwrap(); - - let resolver = Arc::new(create_resolver(dns1_sock, dns2_sock).await); - let resolver_cache = Arc::new(Mutex::new(LruCache::new(1000))); - - let http_resolver = CacheResolver::new(resolver.clone(), resolver_cache.clone()); - let connector = HttpConnector::new_with_resolver(http_resolver); - let https = HttpsConnector::new_with_connector(connector); - let client: HttpClient = Client::builder().build(https); - let gateway = Arc::new(ip_gateway()); - let proxy_port = args.value_of("proxy_port").unwrap().parse::().unwrap(); - let addr = SocketAddr::from((bind_addr.unwrap().ip(), proxy_port)); - - let make_service = make_service_fn(move |_| { - let client = client.clone(); - let gateway = gateway.clone(); - let resolver = resolver.clone(); - let resolver_cache = resolver_cache.clone(); - async move { - Ok::<_, Infallible>(service_fn( - move |req| proxy( - client.clone(), - gateway.clone(), - resolver.clone(), - resolver_cache.clone(), - req) - )) - } - }); - - let server = Server::bind(&addr).serve(make_service); - - println!("Listening on http://{}", addr); - - if let Err(e) = server.await { - eprintln!("server error: {}", e); - } + start_proxy(dns1_ip, dns2_ip, bind_addr.unwrap().ip(), proxy_port); } diff --git a/src/pass.rs b/src/pass.rs index 8b803ab..c205e07 100644 --- a/src/pass.rs +++ b/src/pass.rs @@ -4,47 +4,75 @@ * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ */ +extern crate bcrypt; +extern crate rand; + use std::env; use std::fs; use std::fs::File; use std::path::Path; use std::process::exit; -pub fn init_password (password_file : &str, password_opt : Option<&str>) -> String { +use rand::distributions::Alphanumeric; +use self::rand::Rng; +use std::io::Write; + +pub fn init_password (password_file_name : &str, password_opt : Option<&str>) -> String { if password_opt.is_some() { let password_env_var = password_opt.unwrap(); let password_env_var_result = env::var(password_env_var); if password_env_var_result.is_err() { eprintln!("\nERROR: password-env-var argument was {} but that environment variable was not defined\n", password_env_var); - exit(2); + exit(3); } let password_val = password_env_var_result.unwrap(); if password_val.trim().len() == 0 { eprintln!("\nERROR: password-env-var argument was {} but the value of that environment variable was empty\n", password_env_var); - exit(2); + exit(3); } - let password_path = Path::new(password_file); + let password_path = Path::new(password_file_name); + let password_file_result = File::create(password_path); if password_file_result.is_err() { let err = password_file_result.err(); if err.is_none() { - eprintln!("\nERROR: unknown error writing to password file {}\n", password_file); + eprintln!("\nERROR: unknown error writing to password file {}\n", password_file_name); + } else { + eprintln!("\nERROR: error writing to password file {}: {:?}\n", password_file_name, err); + } + exit(3); + } + let mut password_file = password_file_result.unwrap(); + + let salt = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .collect::(); + let mut bcrypted_pass : [u8; 24] = [0; 24]; + bcrypt::bcrypt(14, &salt.as_bytes(), password_val.as_bytes(), &mut bcrypted_pass); + + let write_result = password_file.write_all(&bcrypted_pass); + if write_result.is_err() { + let err = write_result.err(); + if err.is_none() { + eprintln!("\nERROR: unknown error writing password file {}\n", password_file_name); } else { - eprintln!("\nERROR: error writing to password file {}: {:?}\n", password_file, err); + eprintln!("\nERROR: error writing password file {}: {:?}\n", password_file_name, err.unwrap()); } - exit(2); + exit(3); } } - let result = fs::read_to_string(&password_file); + let result = fs::read_to_string(&password_file_name); if result.is_err() { let err = result.err(); if err.is_none() { - eprintln!("\nERROR: unknown error reading password file {}\n", password_file); + eprintln!("\nERROR: unknown error reading password file {}\n", password_file_name); } else { - eprintln!("\nERROR: error reading password file {}: {:?}\n", password_file, err.unwrap()); + eprintln!("\nERROR: error reading password file {}: {:?}\n", password_file_name, err.unwrap()); } - exit(2); + exit(3); } + return result.unwrap(); -} \ No newline at end of file +} diff --git a/src/proxy.rs b/src/proxy.rs index 5679a02..0aa6b74 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -6,14 +6,16 @@ extern crate lru; -use std::net::SocketAddr; +use std::convert::Infallible; +use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; use futures_util::future::try_join; -use hyper::upgrade::Upgraded; -use hyper::{Body, Client, Method, Request, Response}; +use hyper::{Body, Client, Method, Request, Response, Server}; use hyper::client::HttpConnector; +use hyper::service::{make_service_fn, service_fn}; +use hyper::upgrade::Upgraded; use hyper_tls::HttpsConnector; use lru::LruCache; @@ -27,13 +29,57 @@ use crate::dns_cache::*; use crate::net::*; use crate::hyper_util::bad_request; -//type HttpClient = Client>, hyper::Body>; +type HttpClient = Client>, hyper::Body>; + +pub async fn start_proxy (dns1_ip : &str, + dns2_ip: &str, + proxy_ip: IpAddr, + proxy_port: u16) { + let dns1_sock : SocketAddr = format!("{}:53", dns1_ip).parse().unwrap(); + let dns2_sock : SocketAddr = format!("{}:53", dns2_ip).parse().unwrap(); + + let resolver = Arc::new(create_resolver(dns1_sock, dns2_sock).await); + let resolver_cache = Arc::new(Mutex::new(LruCache::new(1000))); + + let http_resolver = CacheResolver::new(resolver.clone(), resolver_cache.clone()); + let connector = HttpConnector::new_with_resolver(http_resolver); + let https = HttpsConnector::new_with_connector(connector); + let client: HttpClient = Client::builder().build(https); + let gateway = Arc::new(ip_gateway()); + + let addr = SocketAddr::from((proxy_ip, proxy_port)); + + let make_service = make_service_fn(move |_| { + let client = client.clone(); + let gateway = gateway.clone(); + let resolver = resolver.clone(); + let resolver_cache = resolver_cache.clone(); + async move { + Ok::<_, Infallible>(service_fn( + move |req| proxy( + client.clone(), + gateway.clone(), + resolver.clone(), + resolver_cache.clone(), + req) + )) + } + }); + + let server = Server::bind(&addr).serve(make_service); + + println!("Listening on http://{}", addr); + + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } +} -pub async fn proxy(client: Client>>, - gateway: Arc, - resolver: Arc, - resolver_cache: Arc>>, - req: Request) -> Result, hyper::Error> { +async fn proxy(client: Client>>, + gateway: Arc, + resolver: Arc, + resolver_cache: Arc>>, + req: Request) -> Result, hyper::Error> { let host = req.uri().host(); if host.is_none() { return bad_request("No host!");