@@ -87,6 +87,25 @@ dependencies = [ | |||||
"rustc-demangle", | "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]] | [[package]] | ||||
name = "bitflags" | name = "bitflags" | ||||
version = "0.5.0" | version = "0.5.0" | ||||
@@ -99,10 +118,31 @@ version = "1.2.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | 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]] | [[package]] | ||||
name = "bubble-flexrouter" | name = "bubble-flexrouter" | ||||
version = "0.1.0" | version = "0.1.0" | ||||
dependencies = [ | dependencies = [ | ||||
"bcrypt", | |||||
"clap", | "clap", | ||||
"futures", | "futures", | ||||
"futures-channel", | "futures-channel", | ||||
@@ -113,12 +153,19 @@ dependencies = [ | |||||
"hyper-tls", | "hyper-tls", | ||||
"lru", | "lru", | ||||
"pnet", | "pnet", | ||||
"rand", | |||||
"tokio", | "tokio", | ||||
"tower", | "tower", | ||||
"trust-dns-resolver", | "trust-dns-resolver", | ||||
"whoami", | "whoami", | ||||
] | ] | ||||
[[package]] | |||||
name = "byteorder" | |||||
version = "1.3.4" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | |||||
[[package]] | [[package]] | ||||
name = "bytes" | name = "bytes" | ||||
version = "0.5.6" | version = "0.5.6" | ||||
@@ -312,6 +359,15 @@ dependencies = [ | |||||
"slab", | "slab", | ||||
] | ] | ||||
[[package]] | |||||
name = "generic-array" | |||||
version = "0.12.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" | |||||
dependencies = [ | |||||
"typenum", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "getrandom" | name = "getrandom" | ||||
version = "0.1.14" | version = "0.1.14" | ||||
@@ -720,6 +776,12 @@ version = "1.4.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" | ||||
[[package]] | |||||
name = "opaque-debug" | |||||
version = "0.2.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" | |||||
[[package]] | [[package]] | ||||
name = "openssl" | name = "openssl" | ||||
version = "0.10.30" | version = "0.10.30" | ||||
@@ -1511,6 +1573,12 @@ version = "0.2.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" | ||||
[[package]] | |||||
name = "typenum" | |||||
version = "1.12.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" | |||||
[[package]] | [[package]] | ||||
name = "unicode-bidi" | name = "unicode-bidi" | ||||
version = "0.3.4" | version = "0.3.4" | ||||
@@ -6,6 +6,7 @@ authors = ["Jonathan Cobb <jonathan@getbubblenow.com>"] | |||||
edition = "2018" | edition = "2018" | ||||
[dependencies] | [dependencies] | ||||
bcrypt = "0.6.3" | |||||
clap = "2.33.0" | clap = "2.33.0" | ||||
futures = "0.3.5" | futures = "0.3.5" | ||||
futures-core = { version = "0.3", default-features = false } | futures-core = { version = "0.3", default-features = false } | ||||
@@ -16,6 +17,7 @@ hyper = { version = "0.13.7", features = ["stream"] } | |||||
hyper-tls = "0.4.3" | hyper-tls = "0.4.3" | ||||
lru = "0.6.0" | lru = "0.6.0" | ||||
pnet = "0.26.0" | pnet = "0.26.0" | ||||
rand = "0.7.3" | |||||
tokio = { version = "0.2.22", features = ["full"] } | tokio = { version = "0.2.22", features = ["full"] } | ||||
tower = "0.3.1" | tower = "0.3.1" | ||||
trust-dns-resolver = "0.19.5" | trust-dns-resolver = "0.19.5" | ||||
@@ -6,32 +6,16 @@ | |||||
extern crate lru; | extern crate lru; | ||||
use std::convert::Infallible; | |||||
use std::net::SocketAddr; | |||||
use std::process::exit; | use std::process::exit; | ||||
use std::sync::Arc; | |||||
use clap::{Arg, ArgMatches, App}; | 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 pnet::datalink; | ||||
use tokio::sync::Mutex; | |||||
use whoami; | use whoami; | ||||
use bubble_flexrouter::pass::init_password; | 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: | // To try this example: | ||||
// 1. cargo run --example http_proxy | // 1. cargo run --example http_proxy | ||||
@@ -134,7 +118,7 @@ async fn main() { | |||||
let password_file = password_file_opt.unwrap(); | let password_file = password_file_opt.unwrap(); | ||||
let password_opt = args.value_of("password_env_var"); | 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"); | let proxy_ip_opt = args.value_of("proxy_ip"); | ||||
if proxy_ip_opt.is_none() { | if proxy_ip_opt.is_none() { | ||||
@@ -160,44 +144,7 @@ async fn main() { | |||||
} | } | ||||
let dns1_ip = args.value_of("dns1").unwrap(); | 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_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 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/ | * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | ||||
*/ | */ | ||||
extern crate bcrypt; | |||||
extern crate rand; | |||||
use std::env; | use std::env; | ||||
use std::fs; | use std::fs; | ||||
use std::fs::File; | use std::fs::File; | ||||
use std::path::Path; | use std::path::Path; | ||||
use std::process::exit; | 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() { | if password_opt.is_some() { | ||||
let password_env_var = password_opt.unwrap(); | let password_env_var = password_opt.unwrap(); | ||||
let password_env_var_result = env::var(password_env_var); | let password_env_var_result = env::var(password_env_var); | ||||
if password_env_var_result.is_err() { | 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); | 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(); | let password_val = password_env_var_result.unwrap(); | ||||
if password_val.trim().len() == 0 { | 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); | 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); | let password_file_result = File::create(password_path); | ||||
if password_file_result.is_err() { | if password_file_result.is_err() { | ||||
let err = password_file_result.err(); | let err = password_file_result.err(); | ||||
if err.is_none() { | 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 { | } 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() { | if result.is_err() { | ||||
let err = result.err(); | let err = result.err(); | ||||
if err.is_none() { | 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 { | } 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(); | return result.unwrap(); | ||||
} | |||||
} |
@@ -6,14 +6,16 @@ | |||||
extern crate lru; | extern crate lru; | ||||
use std::net::SocketAddr; | |||||
use std::convert::Infallible; | |||||
use std::net::{IpAddr, SocketAddr}; | |||||
use std::sync::Arc; | use std::sync::Arc; | ||||
use futures_util::future::try_join; | 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::client::HttpConnector; | ||||
use hyper::service::{make_service_fn, service_fn}; | |||||
use hyper::upgrade::Upgraded; | |||||
use hyper_tls::HttpsConnector; | use hyper_tls::HttpsConnector; | ||||
use lru::LruCache; | use lru::LruCache; | ||||
@@ -27,13 +29,57 @@ use crate::dns_cache::*; | |||||
use crate::net::*; | use crate::net::*; | ||||
use crate::hyper_util::bad_request; | 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(); | let host = req.uri().host(); | ||||
if host.is_none() { | if host.is_none() { | ||||
return bad_request("No host!"); | return bad_request("No host!"); | ||||