tor: better address status check, bridges configuration
This commit is contained in:
parent
4324acb0f7
commit
460590d531
7 changed files with 280 additions and 55 deletions
|
@ -132,6 +132,8 @@ transport:
|
||||||
tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously.
|
tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously.
|
||||||
tor_sending: 'Sending %{amount} ツ over Tor'
|
tor_sending: 'Sending %{amount} ツ over Tor'
|
||||||
tor_settings: Tor Settings
|
tor_settings: Tor Settings
|
||||||
|
bridges: Bridges
|
||||||
|
bridges_desc: Setup bridges to bypass Tor network censorship if usual connection is not working.
|
||||||
network:
|
network:
|
||||||
self: Network
|
self: Network
|
||||||
type: 'Network type:'
|
type: 'Network type:'
|
||||||
|
|
|
@ -132,6 +132,8 @@ transport:
|
||||||
tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций.
|
tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций.
|
||||||
tor_sending: 'Отправка %{amount} ツ через Tor'
|
tor_sending: 'Отправка %{amount} ツ через Tor'
|
||||||
tor_settings: Настройки Tor
|
tor_settings: Настройки Tor
|
||||||
|
bridges: Мосты
|
||||||
|
bridges_desc: Настройте мосты для обхода цензуры сети Tor, если обычное соединение не работает.
|
||||||
network:
|
network:
|
||||||
self: Сеть
|
self: Сеть
|
||||||
type: 'Тип сети:'
|
type: 'Тип сети:'
|
||||||
|
|
|
@ -293,6 +293,7 @@ impl WalletTransport {
|
||||||
|
|
||||||
// Draw checkbox to enable/disable bridges.
|
// Draw checkbox to enable/disable bridges.
|
||||||
View::checkbox(ui, bridge.is_some(), t!("transport.bridges"), || {
|
View::checkbox(ui, bridge.is_some(), t!("transport.bridges"), || {
|
||||||
|
// Save value.
|
||||||
let value = if bridge.is_some() {
|
let value = if bridge.is_some() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -301,6 +302,13 @@ impl WalletTransport {
|
||||||
Some(b)
|
Some(b)
|
||||||
};
|
};
|
||||||
TorConfig::save_bridge(value);
|
TorConfig::save_bridge(value);
|
||||||
|
// Restart service.
|
||||||
|
if let Ok(key) = wallet.secret_key() {
|
||||||
|
let service_id = &wallet.identifier();
|
||||||
|
Tor::stop_service(service_id);
|
||||||
|
let api_port = wallet.foreign_api_port().unwrap();
|
||||||
|
Tor::start_service(api_port, key, service_id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -330,10 +338,17 @@ impl WalletTransport {
|
||||||
if current_bridge != bridge {
|
if current_bridge != bridge {
|
||||||
TorConfig::save_bridge(Some(bridge.clone()));
|
TorConfig::save_bridge(Some(bridge.clone()));
|
||||||
self.bridge_bin_path_edit = bridge.binary_path();
|
self.bridge_bin_path_edit = bridge.binary_path();
|
||||||
|
// Restart service.
|
||||||
|
if let Ok(key) = wallet.secret_key() {
|
||||||
|
let service_id = &wallet.identifier();
|
||||||
|
Tor::stop_service(service_id);
|
||||||
|
let api_port = wallet.foreign_api_port().unwrap();
|
||||||
|
Tor::start_service(api_port, key, service_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw binary path text edit.
|
// Draw binary path text edit.
|
||||||
let bin_edit_id = Id::from(modal.id).with(wallet.get_config().id).with("_bridge_bin");
|
let bin_edit_id = Id::from(modal.id).with(wallet.get_config().id).with("_bin_edit");
|
||||||
let bin_edit_opts = TextEditOptions::new(bin_edit_id).paste();
|
let bin_edit_opts = TextEditOptions::new(bin_edit_id).paste();
|
||||||
let bin_edit_before = self.bridge_bin_path_edit.clone();
|
let bin_edit_before = self.bridge_bin_path_edit.clone();
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
|
@ -351,6 +366,13 @@ impl WalletTransport {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
TorConfig::save_bridge(Some(b));
|
TorConfig::save_bridge(Some(b));
|
||||||
|
// Restart service.
|
||||||
|
if let Ok(key) = wallet.secret_key() {
|
||||||
|
let service_id = &wallet.identifier();
|
||||||
|
Tor::stop_service(service_id);
|
||||||
|
let api_port = wallet.foreign_api_port().unwrap();
|
||||||
|
Tor::start_service(api_port, key, service_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,27 @@
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
|
use crate::tor::TorBridge;
|
||||||
|
|
||||||
/// Tor configuration.
|
/// Tor configuration.
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct TorConfig {
|
pub struct TorConfig {
|
||||||
// Flag to check if Tor bridges usage is needed.
|
/// Selected bridge type.
|
||||||
pub(crate) use_bridges: Option<bool>
|
bridge: Option<TorBridge>,
|
||||||
|
/// Obfs4 bridge type.
|
||||||
|
obfs4: TorBridge,
|
||||||
|
/// Snowflake bridge type.
|
||||||
|
snowflake: TorBridge,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TorConfig {
|
impl Default for TorConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
use_bridges: Some(false)
|
bridge: None,
|
||||||
|
obfs4: TorBridge::Obfs4(TorBridge::DEFAULT_OBFS4_BIN_PATH.to_string()),
|
||||||
|
snowflake: TorBridge::Snowflake(TorBridge::DEFAULT_SNOWFLAKE_BIN_PATH.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,4 +81,40 @@ impl TorConfig {
|
||||||
base.push(Self::KEYSTORE_DIR);
|
base.push(Self::KEYSTORE_DIR);
|
||||||
base.to_str().unwrap().to_string()
|
base.to_str().unwrap().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save Tor bridge.
|
||||||
|
pub fn save_bridge(bridge: Option<TorBridge>) {
|
||||||
|
let mut w_tor_config = Settings::tor_config_to_update();
|
||||||
|
w_tor_config.bridge = bridge.clone();
|
||||||
|
if bridge.is_some() {
|
||||||
|
let bridge = bridge.unwrap();
|
||||||
|
match &bridge {
|
||||||
|
TorBridge::Snowflake(_) => {
|
||||||
|
w_tor_config.snowflake = bridge
|
||||||
|
}
|
||||||
|
TorBridge::Obfs4(_) => {
|
||||||
|
w_tor_config.obfs4 = bridge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w_tor_config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current Tor bridge if enabled.
|
||||||
|
pub fn get_bridge() -> Option<TorBridge> {
|
||||||
|
let r_config = Settings::tor_config_to_read();
|
||||||
|
r_config.bridge.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get saved Obfs4 bridge.
|
||||||
|
pub fn get_obfs4() -> TorBridge {
|
||||||
|
let r_config = Settings::tor_config_to_read();
|
||||||
|
r_config.obfs4.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get saved Snowflake bridge.
|
||||||
|
pub fn get_snowflake() -> TorBridge {
|
||||||
|
let r_config = Settings::tor_config_to_read();
|
||||||
|
r_config.snowflake.clone()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -16,4 +16,7 @@ mod config;
|
||||||
pub use config::TorConfig;
|
pub use config::TorConfig;
|
||||||
|
|
||||||
mod tor;
|
mod tor;
|
||||||
pub use tor::Tor;
|
pub use tor::Tor;
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
pub use types::*;
|
207
src/tor/tor.rs
207
src/tor/tor.rs
|
@ -14,18 +14,22 @@
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use futures::executor::block_on;
|
use std::time::Duration;
|
||||||
|
use arti_client::config::pt::TransportConfigBuilder;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use futures::task::SpawnExt;
|
use futures::task::SpawnExt;
|
||||||
|
|
||||||
use arti_client::{TorClient, TorClientConfig};
|
use arti_client::{TorClient, TorClientConfig};
|
||||||
use arti_client::config::TorClientConfigBuilder;
|
use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder};
|
||||||
use fs_mistrust::Mistrust;
|
use fs_mistrust::Mistrust;
|
||||||
use grin_util::secp::SecretKey;
|
use grin_util::secp::SecretKey;
|
||||||
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
||||||
use curve25519_dalek::digest::Digest;
|
use curve25519_dalek::digest::Digest;
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
|
use tokio::time::{sleep, sleep_until};
|
||||||
|
use tor_config::CfgPath;
|
||||||
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
|
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
|
||||||
use tor_rtcompat::Runtime;
|
use tor_rtcompat::Runtime;
|
||||||
use tor_hsrproxy::OnionServiceReverseProxy;
|
use tor_hsrproxy::OnionServiceReverseProxy;
|
||||||
|
@ -36,8 +40,7 @@ use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector};
|
||||||
use tor_llcrypto::pk::ed25519::ExpandedKeypair;
|
use tor_llcrypto::pk::ed25519::ExpandedKeypair;
|
||||||
use tor_hscrypto::pk::{HsIdKey, HsIdKeypair};
|
use tor_hscrypto::pk::{HsIdKey, HsIdKeypair};
|
||||||
use arti_hyper::ArtiHttpConnector;
|
use arti_hyper::ArtiHttpConnector;
|
||||||
use futures::TryFutureExt;
|
use hyper::{Body, Uri};
|
||||||
use hyper::Body;
|
|
||||||
use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder};
|
use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder};
|
||||||
|
|
||||||
// On aarch64-apple-darwin targets there is an issue with the native and rustls
|
// On aarch64-apple-darwin targets there is an issue with the native and rustls
|
||||||
|
@ -49,7 +52,6 @@ use tls_api_native_tls::TlsConnector;
|
||||||
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
|
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
|
||||||
use tls_api_openssl::TlsConnector;
|
use tls_api_openssl::TlsConnector;
|
||||||
|
|
||||||
|
|
||||||
use crate::tor::TorConfig;
|
use crate::tor::TorConfig;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -59,9 +61,6 @@ lazy_static! {
|
||||||
|
|
||||||
/// Tor server to use as SOCKS proxy for requests and to launch Onion services.
|
/// Tor server to use as SOCKS proxy for requests and to launch Onion services.
|
||||||
pub struct Tor {
|
pub struct Tor {
|
||||||
/// [`TorClient`] used for connections with configuration.
|
|
||||||
client: Arc<RwLock<(TorClient<TokioNativeTlsRuntime>, TorClientConfig)>>,
|
|
||||||
|
|
||||||
/// Mapping of running Onion services identifiers to proxy.
|
/// Mapping of running Onion services identifiers to proxy.
|
||||||
running_services: Arc<RwLock<BTreeMap<String,
|
running_services: Arc<RwLock<BTreeMap<String,
|
||||||
(Arc<RunningOnionService>, Arc<OnionServiceReverseProxy>)>>>,
|
(Arc<RunningOnionService>, Arc<OnionServiceReverseProxy>)>>>,
|
||||||
|
@ -73,21 +72,7 @@ pub struct Tor {
|
||||||
|
|
||||||
impl Default for Tor {
|
impl Default for Tor {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Create Tor client config.
|
|
||||||
let mut builder =
|
|
||||||
TorClientConfigBuilder::from_directories(TorConfig::state_path(),
|
|
||||||
TorConfig::cache_path());
|
|
||||||
builder.address_filter().allow_onion_addrs(true);
|
|
||||||
|
|
||||||
// Create connected Tor client from config.
|
|
||||||
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
|
||||||
let config = builder.build().unwrap();
|
|
||||||
let client = TorClient::with_runtime(runtime)
|
|
||||||
.config(config.clone())
|
|
||||||
.create_unbootstrapped()
|
|
||||||
.unwrap();
|
|
||||||
Self {
|
Self {
|
||||||
client: Arc::new(RwLock::new((client, config))),
|
|
||||||
running_services: Arc::new(RwLock::new(BTreeMap::new())),
|
running_services: Arc::new(RwLock::new(BTreeMap::new())),
|
||||||
starting_services: Arc::new(RwLock::new(BTreeSet::new())),
|
starting_services: Arc::new(RwLock::new(BTreeSet::new())),
|
||||||
failed_services: Arc::new(RwLock::new(BTreeSet::new()))
|
failed_services: Arc::new(RwLock::new(BTreeSet::new()))
|
||||||
|
@ -96,25 +81,46 @@ impl Default for Tor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tor {
|
impl Tor {
|
||||||
|
async fn build_client(runtime: TokioNativeTlsRuntime)
|
||||||
|
-> (TorClient<TokioNativeTlsRuntime>, TorClientConfig) {
|
||||||
|
// Create Tor client config.
|
||||||
|
let mut builder =
|
||||||
|
TorClientConfigBuilder::from_directories(TorConfig::state_path(),
|
||||||
|
TorConfig::cache_path());
|
||||||
|
// Setup bridges.
|
||||||
|
let bridge = TorConfig::get_bridge();
|
||||||
|
if let Some(b) = bridge {
|
||||||
|
match b {
|
||||||
|
super::TorBridge::Snowflake(path) => Self::build_snowflake(&mut builder, path),
|
||||||
|
super::TorBridge::Obfs4(path) => Self::build_obfs4(&mut builder, path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Setup address filter.
|
||||||
|
builder.address_filter().allow_onion_addrs(true);
|
||||||
|
// Create connected Tor client from config.
|
||||||
|
let config = builder.build().unwrap();
|
||||||
|
(TorClient::with_runtime(runtime)
|
||||||
|
.config(config.clone())
|
||||||
|
.create_bootstrapped()
|
||||||
|
.await
|
||||||
|
.unwrap(), config)
|
||||||
|
}
|
||||||
|
|
||||||
/// Send post request using Tor.
|
/// Send post request using Tor.
|
||||||
pub async fn post(body: String, url: String) -> Option<String> {
|
pub async fn post(body: String, url: String) -> Option<String> {
|
||||||
// Bootstrap client.
|
// Create client.
|
||||||
let client_config = TOR_SERVER_STATE.client.read().unwrap();
|
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
||||||
let client = client_config.0.clone();
|
let (client, _) = Self::build_client(runtime).await;
|
||||||
client.bootstrap().await.unwrap();
|
|
||||||
|
|
||||||
// Create http tor-powered client to post data.
|
// Create http tor-powered client to post data.
|
||||||
let tls_connector = TlsConnector::builder().unwrap().build().unwrap();
|
let tls_connector = TlsConnector::builder().unwrap().build().unwrap();
|
||||||
let tor_connector = ArtiHttpConnector::new(client, tls_connector);
|
let tor_connector = ArtiHttpConnector::new(client, tls_connector);
|
||||||
let http = hyper::Client::builder().build::<_, Body>(tor_connector);
|
let http = hyper::Client::builder().build::<_, Body>(tor_connector);
|
||||||
|
|
||||||
// Create request.
|
// Create request.
|
||||||
let req = hyper::Request::builder()
|
let req = hyper::Request::builder()
|
||||||
.method(hyper::Method::POST)
|
.method(hyper::Method::POST)
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.body(Body::from(body))
|
.body(Body::from(body))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Send request.
|
// Send request.
|
||||||
let mut resp = None;
|
let mut resp = None;
|
||||||
match http.request(req).await {
|
match http.request(req).await {
|
||||||
|
@ -173,33 +179,62 @@ impl Tor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let service_id = id.clone();
|
let service_id = id.clone();
|
||||||
let client_config = TOR_SERVER_STATE.client.read().unwrap();
|
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
||||||
let client = client_config.0.clone();
|
let runtime_client = runtime.clone();
|
||||||
let config = client_config.1.clone();
|
runtime.spawn(async move {
|
||||||
client.clone().runtime().spawn(async move {
|
let (client, config) = Self::build_client(runtime_client.clone()).await;
|
||||||
// Add service key to keystore.
|
// Add service key to keystore.
|
||||||
let hs_nickname = HsNickname::new(service_id.clone()).unwrap();
|
let hs_nickname = HsNickname::new(service_id.clone()).unwrap();
|
||||||
Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname);
|
Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname);
|
||||||
|
// Launch Onion service.
|
||||||
// Bootstrap client and launch Onion service.
|
|
||||||
client.bootstrap().await.unwrap();
|
|
||||||
let service_config = OnionServiceConfigBuilder::default()
|
let service_config = OnionServiceConfigBuilder::default()
|
||||||
.nickname(hs_nickname.clone())
|
.nickname(hs_nickname.clone())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (service, request) = client.launch_onion_service(service_config).unwrap();
|
let (service, request) = client.launch_onion_service(service_config).unwrap();
|
||||||
|
|
||||||
|
// Check service availability.
|
||||||
|
let service_check = service.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let runtime = TokioNativeTlsRuntime::create().unwrap();
|
||||||
|
let runtime_client = runtime.clone();
|
||||||
|
runtime.spawn(async move {
|
||||||
|
loop {
|
||||||
|
// Create client.
|
||||||
|
let (client, _) = Self::build_client(runtime_client.clone()).await;
|
||||||
|
|
||||||
|
// Create http tor-powered client to ping service.
|
||||||
|
let tls_connector = TlsConnector::builder().unwrap().build().unwrap();
|
||||||
|
let tor_connector = ArtiHttpConnector::new(client, tls_connector);
|
||||||
|
let http = hyper::Client::builder().build::<_, Body>(tor_connector);
|
||||||
|
|
||||||
|
let url = format!("http://{}", service_check.onion_name().unwrap().to_string());
|
||||||
|
match http.get(Uri::from_str(url.as_str()).unwrap()).await {
|
||||||
|
Ok(_) => {
|
||||||
|
// Remove service from starting.
|
||||||
|
let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap();
|
||||||
|
w_services.remove(&service_id);
|
||||||
|
|
||||||
|
println!("success");
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
// Put service to starting.
|
||||||
|
let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap();
|
||||||
|
w_services.insert(service_id.clone());
|
||||||
|
|
||||||
|
println!("err: {}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(5000)).await;
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
// Launch service proxy.
|
// Launch service proxy.
|
||||||
let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port);
|
let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port);
|
||||||
tokio::spawn(
|
tokio::spawn(
|
||||||
Self::run_service_proxy(addr, client, service.clone(), request, hs_nickname.clone())
|
Self::run_service_proxy(addr, client, service.clone(), request, hs_nickname.clone())
|
||||||
).await.unwrap();
|
).await.unwrap();
|
||||||
|
|
||||||
println!(
|
|
||||||
"Onion service {} launched at: {}",
|
|
||||||
hs_nickname,
|
|
||||||
service.onion_name().unwrap().to_string()
|
|
||||||
);
|
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,10 +265,6 @@ impl Tor {
|
||||||
// Save running service.
|
// Save running service.
|
||||||
let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap();
|
let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap();
|
||||||
w_services.insert(id.clone(), (service.clone(), proxy.clone()));
|
w_services.insert(id.clone(), (service.clone(), proxy.clone()));
|
||||||
|
|
||||||
// Remove service from starting.
|
|
||||||
let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap();
|
|
||||||
w_services.remove(&id);
|
|
||||||
|
|
||||||
// Start proxy for launched service.
|
// Start proxy for launched service.
|
||||||
client
|
client
|
||||||
|
@ -247,8 +278,6 @@ impl Tor {
|
||||||
let mut w_services =
|
let mut w_services =
|
||||||
TOR_SERVER_STATE.running_services.write().unwrap();
|
TOR_SERVER_STATE.running_services.write().unwrap();
|
||||||
w_services.remove(&id);
|
w_services.remove(&id);
|
||||||
|
|
||||||
println!("Onion service {} stopped.", nickname);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Remove service from running.
|
// Remove service from running.
|
||||||
|
@ -259,8 +288,6 @@ impl Tor {
|
||||||
let mut w_services =
|
let mut w_services =
|
||||||
TOR_SERVER_STATE.failed_services.write().unwrap();
|
TOR_SERVER_STATE.failed_services.write().unwrap();
|
||||||
w_services.insert(id);
|
w_services.insert(id);
|
||||||
|
|
||||||
eprintln!("Onion service {} exited with an error: {}", nickname, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
@ -305,4 +332,84 @@ impl Tor {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_snowflake(builder: &mut TorClientConfigBuilder, bin_path: String) {
|
||||||
|
let mut bridges = vec![];
|
||||||
|
// Add a single bridge to the list of bridges, from a bridge line.
|
||||||
|
// This line comes from https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/blob/main/projects/common/bridges_list.snowflake.txt
|
||||||
|
// this is a real bridge line you can use as-is, after making sure it's still up to date with
|
||||||
|
// above link.
|
||||||
|
const BRIDGE1_LINE : &str = "Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn";
|
||||||
|
let bridge_1: BridgeConfigBuilder = BRIDGE1_LINE.parse().unwrap();
|
||||||
|
bridges.push(bridge_1);
|
||||||
|
|
||||||
|
// Add a second bridge, built by hand. We use the 2nd bridge line from above, but modify some
|
||||||
|
// parameters to use AMP Cache instead of Fastly as a signaling channel. The difference in
|
||||||
|
// configuration is detailed in
|
||||||
|
// https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/client#amp-cache
|
||||||
|
let mut bridge2_builder = BridgeConfigBuilder::default();
|
||||||
|
bridge2_builder
|
||||||
|
.transport("snowflake")
|
||||||
|
.push_setting(
|
||||||
|
"fingerprint",
|
||||||
|
"8838024498816A039FCBBAB14E6F40A0843051FA"
|
||||||
|
)
|
||||||
|
.push_setting("url", "https://snowflake-broker.torproject.net/")
|
||||||
|
.push_setting("ampcache", "https://cdn.ampproject.org/")
|
||||||
|
.push_setting("front", "www.google.com")
|
||||||
|
.push_setting(
|
||||||
|
"ice",
|
||||||
|
"stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478",
|
||||||
|
)
|
||||||
|
.push_setting("utls-imitate", "hellorandomizedalpn");
|
||||||
|
bridge2_builder.set_addrs(vec!["192.0.2.4:80".parse().unwrap()]);
|
||||||
|
bridge2_builder.set_ids(vec!["8838024498816A039FCBBAB14E6F40A0843051FA".parse().unwrap()]);
|
||||||
|
// Now insert the second bridge into our config builder.
|
||||||
|
bridges.push(bridge2_builder);
|
||||||
|
|
||||||
|
// Set bridges to client config builder.
|
||||||
|
builder.bridges().set_bridges(bridges);
|
||||||
|
|
||||||
|
// Now configure an snowflake transport. (Requires the "pt-client" feature)
|
||||||
|
let mut transport = TransportConfigBuilder::default();
|
||||||
|
transport
|
||||||
|
.protocols(vec!["snowflake".parse().unwrap()])
|
||||||
|
// this might be named differently on some systems, this should work on Debian, but Archlinux is known to use `snowflake-pt-client` instead for instance.
|
||||||
|
.path(CfgPath::new("snowflake-client".into()))
|
||||||
|
.run_on_startup(true);
|
||||||
|
builder.bridges().set_transports(vec![transport]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_obfs4(builder: &mut TorClientConfigBuilder, bin_path: String) {
|
||||||
|
// This bridge line is made up for demonstration, and won't work.
|
||||||
|
const BRIDGE1_LINE : &str = "Bridge obfs4 192.0.2.55:38114 316E643333645F6D79216558614D3931657A5F5F cert=YXJlIGZyZXF1ZW50bHkgZnVsbCBvZiBsaXR0bGUgbWVzc2FnZXMgeW91IGNhbiBmaW5kLg iat-mode=0";
|
||||||
|
let bridge_1: BridgeConfigBuilder = BRIDGE1_LINE.parse().unwrap();
|
||||||
|
// This is where we pass `BRIDGE1_LINE` into the BridgeConfigBuilder.
|
||||||
|
builder.bridges().bridges().push(bridge_1);
|
||||||
|
|
||||||
|
// Add a second bridge, built by hand. This way is harder.
|
||||||
|
// This bridge is made up for demonstration, and won't work.
|
||||||
|
let mut bridge2_builder = BridgeConfigBuilder::default();
|
||||||
|
bridge2_builder
|
||||||
|
.transport("obfs4")
|
||||||
|
.push_setting("iat-mode", "1")
|
||||||
|
.push_setting(
|
||||||
|
"cert",
|
||||||
|
"YnV0IHNvbWV0aW1lcyB0aGV5IGFyZSByYW5kb20u8x9aQG/0cIIcx0ItBcTqiSXotQne+Q"
|
||||||
|
);
|
||||||
|
bridge2_builder.set_addrs(vec!["198.51.100.25:443".parse().unwrap()]);
|
||||||
|
bridge2_builder.set_ids(vec!["7DD62766BF2052432051D7B7E08A22F7E34A4543".parse().unwrap()]);
|
||||||
|
// Now insert the second bridge into our config builder.
|
||||||
|
builder.bridges().bridges().push(bridge2_builder);
|
||||||
|
|
||||||
|
// Now configure an obfs4 transport. (Requires the "pt-client" feature)
|
||||||
|
let mut transport = TransportConfigBuilder::default();
|
||||||
|
transport
|
||||||
|
.protocols(vec!["obfs4".parse().unwrap()])
|
||||||
|
// Specify either the name or the absolute path of pluggable transport client binary, this
|
||||||
|
// may differ from system to system.
|
||||||
|
.path(CfgPath::new("/usr/bin/obfs4proxy".into()))
|
||||||
|
.run_on_startup(true);
|
||||||
|
builder.bridges().transports().push(transport);
|
||||||
|
}
|
||||||
}
|
}
|
45
src/tor/types.rs
Normal file
45
src/tor/types.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2024 The Grim Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Tor network bridge type with binary path.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub enum TorBridge {
|
||||||
|
Snowflake(String),
|
||||||
|
Obfs4(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TorBridge {
|
||||||
|
/// Default Snowflake protocol client binary path.
|
||||||
|
pub const DEFAULT_SNOWFLAKE_BIN_PATH: &'static str = "/usr/bin/snowflake-client";
|
||||||
|
/// Default Obfs4 protocol proxy client binary path.
|
||||||
|
pub const DEFAULT_OBFS4_BIN_PATH: &'static str = "/usr/bin/obfs4proxy";
|
||||||
|
|
||||||
|
/// Get bridge protocol name.
|
||||||
|
pub fn protocol_name(&self) -> String {
|
||||||
|
match *self {
|
||||||
|
TorBridge::Snowflake(_) => "snowflake".to_string(),
|
||||||
|
TorBridge::Obfs4(_) => "obfs4".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get bridge client binary path.
|
||||||
|
pub fn binary_path(&self) -> String {
|
||||||
|
match self {
|
||||||
|
TorBridge::Snowflake(path) => path.clone(),
|
||||||
|
TorBridge::Obfs4(path) => path.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue