2020-10-19 23:33:32 -04:00
|
|
|
#[cfg(feature = "tor")]
|
|
|
|
mod tor_test {
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
use anyhow::Result;
|
|
|
|
use hyper::service::{make_service_fn, service_fn};
|
|
|
|
use reqwest::StatusCode;
|
|
|
|
use spectral::prelude::*;
|
2020-10-21 02:53:49 -04:00
|
|
|
use std::{convert::Infallible, fs};
|
2020-10-22 19:32:48 -04:00
|
|
|
use swap::tor::UnauthenticatedConnection;
|
2020-10-21 02:53:49 -04:00
|
|
|
use tempfile::{Builder, NamedTempFile};
|
2020-10-19 23:33:32 -04:00
|
|
|
use tokio::sync::oneshot::Receiver;
|
2020-10-20 01:14:10 -04:00
|
|
|
use torut::{
|
|
|
|
onion::TorSecretKeyV3,
|
|
|
|
utils::{run_tor, AutoKillChild},
|
|
|
|
};
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
async fn hello_world(
|
|
|
|
_req: hyper::Request<hyper::Body>,
|
|
|
|
) -> Result<hyper::Response<hyper::Body>, Infallible> {
|
|
|
|
Ok(hyper::Response::new("Hello World".into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_test_service(port: u16, rx: Receiver<()>) {
|
|
|
|
let make_svc =
|
|
|
|
make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(hello_world)) });
|
|
|
|
let addr = ([127, 0, 0, 1], port).into();
|
|
|
|
let server = hyper::Server::bind(&addr).serve(make_svc);
|
|
|
|
let graceful = server.with_graceful_shutdown(async {
|
|
|
|
rx.await.ok();
|
|
|
|
});
|
|
|
|
tokio::spawn(async {
|
|
|
|
if let Err(e) = graceful.await {
|
|
|
|
eprintln!("server error: {}", e);
|
|
|
|
}
|
|
|
|
});
|
2020-10-21 02:53:49 -04:00
|
|
|
|
|
|
|
tracing::info!("Test server started at port: {}", port);
|
2020-10-19 23:33:32 -04:00
|
|
|
}
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-21 02:53:49 -04:00
|
|
|
fn run_tmp_tor() -> Result<(AutoKillChild, u16, u16, NamedTempFile)> {
|
|
|
|
// we create an empty torrc file to not use the system one
|
|
|
|
let temp_torrc = Builder::new().tempfile()?;
|
|
|
|
let torrc_file = format!("{}", fs::canonicalize(temp_torrc.path())?.display());
|
|
|
|
tracing::info!("Temp torrc file created at: {}", torrc_file);
|
|
|
|
|
2020-10-20 01:36:47 -04:00
|
|
|
let control_port = if port_check::is_local_port_free(9051) {
|
|
|
|
9051
|
|
|
|
} else {
|
|
|
|
port_check::free_local_port().unwrap()
|
|
|
|
};
|
|
|
|
let proxy_port = if port_check::is_local_port_free(9050) {
|
|
|
|
9050
|
|
|
|
} else {
|
|
|
|
port_check::free_local_port().unwrap()
|
|
|
|
};
|
2020-10-20 01:14:10 -04:00
|
|
|
|
2020-10-21 02:53:49 -04:00
|
|
|
let child = run_tor(
|
|
|
|
"tor",
|
|
|
|
&mut [
|
|
|
|
"--CookieAuthentication",
|
|
|
|
"1",
|
|
|
|
"--ControlPort",
|
|
|
|
control_port.to_string().as_str(),
|
|
|
|
"--SocksPort",
|
|
|
|
proxy_port.to_string().as_str(),
|
|
|
|
"-f",
|
|
|
|
&torrc_file,
|
|
|
|
]
|
|
|
|
.iter(),
|
|
|
|
)?;
|
|
|
|
tracing::info!("Tor running with pid: {}", child.id());
|
|
|
|
let child = AutoKillChild::new(child);
|
|
|
|
Ok((child, control_port, proxy_port, temp_torrc))
|
2020-10-20 01:14:10 -04:00
|
|
|
}
|
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_tor_control_port() -> Result<()> {
|
2020-10-20 01:14:10 -04:00
|
|
|
// start tmp tor
|
2020-10-21 02:53:49 -04:00
|
|
|
let (_child, control_port, proxy_port, _tmp_torrc) = run_tmp_tor()?;
|
2020-10-20 01:14:10 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
// Setup test HTTP Server
|
|
|
|
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
|
|
|
|
let port = 8080;
|
|
|
|
start_test_service(port, rx);
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
// Connect to local Tor service
|
2020-10-20 01:14:10 -04:00
|
|
|
let mut authenticated_connection =
|
2020-10-20 01:36:47 -04:00
|
|
|
UnauthenticatedConnection::with_ports(proxy_port, control_port)
|
|
|
|
.init_authenticated_connection()
|
2020-10-20 01:14:10 -04:00
|
|
|
.await?;
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-21 02:53:49 -04:00
|
|
|
tracing::info!("Tor authenticated.");
|
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
// Expose an onion service that re-directs to the echo server.
|
|
|
|
let tor_secret_key_v3 = TorSecretKeyV3::generate();
|
|
|
|
authenticated_connection
|
|
|
|
.add_service(port, &tor_secret_key_v3)
|
|
|
|
.await?;
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
// Test if Tor service forwards to HTTP Server
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-20 01:14:10 -04:00
|
|
|
let proxy = reqwest::Proxy::all(format!("socks5h://127.0.0.1:{}", proxy_port).as_str())
|
2020-10-19 23:33:32 -04:00
|
|
|
.expect("tor proxy should be there");
|
2020-10-20 18:55:47 -04:00
|
|
|
let client = reqwest::Client::builder().proxy(proxy).build()?;
|
2020-10-19 23:33:32 -04:00
|
|
|
let onion_address = tor_secret_key_v3.public().get_onion_address().to_string();
|
|
|
|
let onion_url = format!("http://{}:8080", onion_address);
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-21 02:53:49 -04:00
|
|
|
tracing::info!("Tor service added: {}", onion_url);
|
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
let res = client.get(&onion_url).send().await?;
|
2020-10-21 02:53:49 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
assert_that(&res.status()).is_equal_to(StatusCode::OK);
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
let text = res.text().await?;
|
|
|
|
assert_that!(text).contains("Hello World");
|
2020-10-21 02:53:49 -04:00
|
|
|
tracing::info!(
|
|
|
|
"Local server called via Tor proxy. Its response is: {}",
|
|
|
|
text
|
|
|
|
);
|
2020-10-19 20:43:13 -04:00
|
|
|
|
2020-10-19 23:33:32 -04:00
|
|
|
// gracefully shut down server
|
|
|
|
let _ = tx.send(());
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-10-19 20:43:13 -04:00
|
|
|
}
|