@@ -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" | |||
@@ -6,6 +6,7 @@ authors = ["Jonathan Cobb <jonathan@getbubblenow.com>"] | |||
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" | |||
@@ -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_tls::HttpsConnector<HttpConnector<CacheResolver>>, 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::<u16>().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); | |||
} |
@@ -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::<String>(); | |||
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(); | |||
} | |||
} |
@@ -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_tls::HttpsConnector<HttpConnector<CacheResolver>>, hyper::Body>; | |||
type HttpClient = Client<hyper_tls::HttpsConnector<HttpConnector<CacheResolver>>, 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<HttpsConnector<HttpConnector<CacheResolver>>>, | |||
gateway: Arc<String>, | |||
resolver: Arc<TokioAsyncResolver>, | |||
resolver_cache: Arc<Mutex<LruCache<String, String>>>, | |||
req: Request<Body>) -> Result<Response<Body>, hyper::Error> { | |||
async fn proxy(client: Client<HttpsConnector<HttpConnector<CacheResolver>>>, | |||
gateway: Arc<String>, | |||
resolver: Arc<TokioAsyncResolver>, | |||
resolver_cache: Arc<Mutex<LruCache<String, String>>>, | |||
req: Request<Body>) -> Result<Response<Body>, hyper::Error> { | |||
let host = req.uri().host(); | |||
if host.is_none() { | |||
return bad_request("No host!"); | |||