Optional Tor Send/Listen Functionality (#226)

* udpate for beta release

* initial tor explorations

* rustfmt

* basic tor tx send working

* rustfmt

* add tor proxy info to config file

* rustfmt

* add utilities to output tor hidden service configuration files

* output tor config as part of listener startup

* rustfmt

* fully automate config and startup of tor process

* rustfmt

* remove unnecessary process kill commands from listener

* rustfmt

* assume defaults for tor sending config if section doesn't exist in grin-wallet.toml

* rustfmt

* ignore tor dev test

* update default paths output by config, compilation + confirmed working on windows

* rustfmt

* fix on osx/unix

* add timeout to tor connector, remove unwrap in client

* allow specifiying tor address without 'http://[].onion' on the command line

* fix api test

* rustfmt

* update address derivation path as per spec

* rustfmt

* move tor init to separate function

* rustfmt

* re-ignore tor dev test

* listen on tor by default if tor available

* rustfmt

* test fix

* remove explicit send via tor flag, and assume tor if address fits

* rustfmt
This commit is contained in:
Yeastplume
2019-10-14 20:24:09 +01:00
committed by GitHub
parent c60301946f
commit b4eeb50c66
34 changed files with 2311 additions and 153 deletions
+73 -14
View File
@@ -13,15 +13,25 @@
// limitations under the License.
/// HTTP Wallet 'plugin' implementation
use crate::api;
use crate::client_utils::{Client, ClientError};
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::SlateSender;
use serde::Serialize;
use serde_json::{json, Value};
use std::net::SocketAddr;
use std::path::MAIN_SEPARATOR;
use crate::tor::config as tor_config;
use crate::tor::process as tor_process;
const TOR_CONFIG_PATH: &'static str = "tor/sender";
#[derive(Clone)]
pub struct HttpSlateSender {
base_url: String,
use_socks: bool,
socks_proxy_addr: Option<SocketAddr>,
tor_config_dir: String,
}
impl HttpSlateSender {
@@ -32,10 +42,27 @@ impl HttpSlateSender {
} else {
Ok(HttpSlateSender {
base_url: base_url.to_owned(),
use_socks: false,
socks_proxy_addr: None,
tor_config_dir: String::from(""),
})
}
}
/// Switch to using socks proxy
pub fn with_socks_proxy(
base_url: &str,
proxy_addr: &str,
tor_config_dir: &str,
) -> Result<HttpSlateSender, SchemeNotHttp> {
let mut ret = Self::new(base_url)?;
ret.use_socks = true;
//TODO: Unwrap
ret.socks_proxy_addr = Some(SocketAddr::V4(proxy_addr.parse().unwrap()));
ret.tor_config_dir = tor_config_dir.into();
Ok(ret)
}
/// Check version of the listening wallet
fn check_other_version(&self, url: &str) -> Result<(), Error> {
let req = json!({
@@ -45,7 +72,7 @@ impl HttpSlateSender {
"params": []
});
let res: String = post(url, None, &req).map_err(|e| {
let res: String = self.post(url, None, req).map_err(|e| {
let mut report = format!("Performing version check (is recipient listening?): {}", e);
let err_string = format!("{}", e);
if err_string.contains("404") {
@@ -92,6 +119,25 @@ impl HttpSlateSender {
Ok(())
}
fn post<IN>(
&self,
url: &str,
api_secret: Option<String>,
input: IN,
) -> Result<String, ClientError>
where
IN: Serialize,
{
let mut client = Client::new();
if self.use_socks {
client.use_socks = true;
client.socks_proxy_addr = self.socks_proxy_addr.clone();
}
let req = client.create_post_request(url, api_secret, &input)?;
let res = client.send_request(req)?;
Ok(res)
}
}
impl SlateSender for HttpSlateSender {
@@ -102,7 +148,30 @@ impl SlateSender for HttpSlateSender {
};
let url_str = format!("{}{}v2/foreign", self.base_url, trailing);
debug!("Posting transaction slate to {}", url_str);
// set up tor send process if needed
let mut tor = tor_process::TorProcess::new();
if self.use_socks {
let tor_dir = format!(
"{}{}{}",
&self.tor_config_dir, MAIN_SEPARATOR, TOR_CONFIG_PATH
);
warn!(
"Starting TOR Process for send at {:?}",
self.socks_proxy_addr
);
tor_config::output_tor_sender_config(
&tor_dir,
&self.socks_proxy_addr.unwrap().to_string(),
)
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
// Start TOR process
tor.torrc_path(&format!("{}/torrc", &tor_dir))
.working_dir(&tor_dir)
.timeout(20)
.completion_percent(100)
.launch()
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e).into()))?;
}
self.check_other_version(&url_str)?;
@@ -119,7 +188,7 @@ impl SlateSender for HttpSlateSender {
});
trace!("Sending receive_tx request: {}", req);
let res: String = post(&url_str, None, &req).map_err(|e| {
let res: String = self.post(&url_str, None, req).map_err(|e| {
let report = format!("Posting transaction slate (is recipient listening?): {}", e);
error!("{}", report);
ErrorKind::ClientCallback(report)
@@ -154,13 +223,3 @@ impl Into<Error> for SchemeNotHttp {
ErrorKind::GenericError(err_str).into()
}
}
pub fn post<IN>(url: &str, api_secret: Option<String>, input: &IN) -> Result<String, api::Error>
where
IN: Serialize,
{
// TODO: change create_post_request to accept a url instead of a &str
let req = api::client::create_post_request(url, api_secret, input)?;
let res = api::client::send_request(req)?;
Ok(res)
}
+34 -5
View File
@@ -13,15 +13,16 @@
// limitations under the License.
mod file;
mod http;
pub mod http;
mod keybase;
pub use self::file::PathToSlate;
pub use self::http::HttpSlateSender;
pub use self::http::{HttpSlateSender, SchemeNotHttp};
pub use self::keybase::{KeybaseAllChannels, KeybaseChannel};
use crate::config::WalletConfig;
use crate::config::{TorConfig, WalletConfig};
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::tor::config::complete_tor_address;
use crate::util::ZeroingString;
/// Sends transactions to a corresponding SlateReceiver
@@ -57,15 +58,43 @@ pub trait SlateGetter {
}
/// select a SlateSender based on method and dest fields from, e.g., SendArgs
pub fn create_sender(method: &str, dest: &str) -> Result<Box<dyn SlateSender>, Error> {
pub fn create_sender(
method: &str,
dest: &str,
tor_config: Option<TorConfig>,
) -> Result<Box<dyn SlateSender>, Error> {
let invalid = || {
ErrorKind::WalletComms(format!(
"Invalid wallet comm type and destination. method: {}, dest: {}",
method, dest
))
};
let mut method = method.into();
// will test if this is a tor address and fill out
// the http://[].onion if missing
let dest = match complete_tor_address(dest) {
Ok(d) => {
method = "tor";
d
}
Err(_) => dest.into(),
};
Ok(match method {
"http" => Box::new(HttpSlateSender::new(dest).map_err(|_| invalid())?),
"http" => Box::new(HttpSlateSender::new(&dest).map_err(|_| invalid())?),
"tor" => match tor_config {
None => {
return Err(
ErrorKind::WalletComms("Tor Configuration required".to_string()).into(),
);
}
Some(tc) => Box::new(
HttpSlateSender::with_socks_proxy(&dest, &tc.socks_proxy_addr, &tc.send_config_dir)
.map_err(|_| invalid())?,
),
},
"keybase" => Box::new(KeybaseChannel::new(dest.to_owned())?),
"self" => {
return Err(ErrorKind::WalletComms(