diff --git a/Cargo.toml b/Cargo.toml index bc68dc18..4e139930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,7 +91,7 @@ uuid = { version = "0.8.2", features = ["v4"] } num-bigint = "0.4.6" ## tor -arti-client = { version = "0.38.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client"] } +arti-client = { version = "0.38.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client", "experimental-api", "bridge-client"] } tor-rtcompat = { version = "0.38.0", features = ["static"] } tor-config = "0.38.0" fs-mistrust = "0.13.1" diff --git a/src/lib.rs b/src/lib.rs index a0a585c3..aaed0fff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,6 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg(target_os = "android")] #[no_mangle] fn android_main(app: AndroidApp) { - #[cfg(debug_assertions)] { std::env::set_var("RUST_BACKTRACE", "full"); let log_config = android_logger::Config::default() diff --git a/src/tor/tor.rs b/src/tor/tor.rs index 8d77c578..04338d80 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -100,22 +100,26 @@ impl Default for Tor { } impl Tor { - /// Create Tor client configuration. - fn build_config(bridges: Option>) -> TorClientConfig { + /// Create Tor configuration returning unused bridges (exclude more than 2 to avoid stuck). + fn build_config(bridges: Option>) -> (TorClientConfig, Vec) { let mut builder = TorClientConfigBuilder::from_directories( TorConfig::state_path(), TorConfig::cache_path(), ); // Build bridges. - if let Some(brs) = bridges { - for b in brs { - Self::build_bridge(&mut builder, b); - } + let bridges = bridges.unwrap_or(vec![]); + let max_two_bridges = if bridges.len() > 2 { + bridges.iter().take(2).map(|b| b.clone()).collect::>() + } else { + bridges.clone() + }; + for b in max_two_bridges { + Self::build_bridge(&mut builder, b); } builder.address_filter().allow_onion_addrs(true); // Create config. let config = builder.build().unwrap(); - config + (config, bridges.iter().map(|b| b.clone()).collect::>()) } /// Build bootstrapped client from provided config. @@ -163,9 +167,13 @@ impl Tor { return; } } + // Cleanup keys, state and cache. + fs::remove_dir_all(TorConfig::keystore_path()).unwrap_or_default(); + fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default(); + fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default(); TOR_STATE.client_launching.store(true, Ordering::Relaxed); - // Setup config. - let config = if let Some(b) = TorConfig::get_bridge() { + // Get initial bridges. + let initial_bridges = if let Some(b) = TorConfig::get_bridge() { let lines_parse = serde_json::from_str::>(&b.connection_line()); let bridges = if let Ok(lines) = lines_parse { lines.iter() @@ -176,37 +184,51 @@ impl Tor { .map(|l| TorBridge::Webtunnel(b.binary_path(), l.to_string())) .collect() }; - Self::build_config(Some(bridges)) + Some(bridges) } else { - Self::build_config(None) + None }; - // Launch client. - let client = Self::build_client_bootstrap(config.clone()); - if let Some(c) = client { - TOR_STATE.client_config.write().replace((c, config)); - thread::sleep(Duration::from_millis(5000)); - TOR_STATE.client_launching.store(false, Ordering::Relaxed); - } else { - // Launch client with default Webtunnel bridges if failed. - let add_bridges = TorBridge::DEFAULT_WEBTUNNEL_CONN_LINES.iter() - .map(|b| TorBridge::Webtunnel(TorConfig::webtunnel_path(), b.to_string())) - .collect::>(); - let config = Self::build_config(Some(add_bridges)); + // Bootstrap client in the loop, trying different bridges. + let mut default_attempt = false; + let mut config_bridges = Self::build_config(initial_bridges); + loop { + let (config, unused_bridges) = config_bridges.clone(); let client = Self::build_client_bootstrap(config.clone()); if let Some(c) = client { - TOR_STATE.client_config.write().replace((c, config)); - thread::sleep(Duration::from_millis(5000)); - TOR_STATE.client_launching.store(false, Ordering::Relaxed); - } else if TorConfig::get_bridge().is_some() { - // Launch without bridges if all attempts failed. - let config = Self::build_config(None); - let client = Self::build_client_bootstrap(config.clone()); - if let Some(c) = client { - TOR_STATE.client_config.write().replace((c, config)); - thread::sleep(Duration::from_millis(5000)); + TOR_STATE.client_config.write().replace((c, config.clone())); + break; + } else { + if !unused_bridges.is_empty() { + config_bridges = Self::build_config(Some(unused_bridges)); + continue; + } + // Cleanup state and cache. + fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default(); + fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default(); + if !default_attempt { + default_attempt = true; + // Launch client with default Webtunnel bridges if failed. + let add_bridges = TorBridge::DEFAULT_WEBTUNNEL_CONN_LINES.iter() + .map(|b| TorBridge::Webtunnel(TorConfig::webtunnel_path(), b.to_string())) + .collect::>(); + config_bridges = Self::build_config(Some(add_bridges)); + continue; + } else if TorConfig::get_bridge().is_some() { + // Cleanup state and cache. + fs::remove_dir_all(TorConfig::state_path()).unwrap_or_default(); + fs::remove_dir_all(TorConfig::cache_path()).unwrap_or_default(); + // Launch without bridges if all attempts failed. + let (config, _) = Self::build_config(None); + let client = Self::build_client_bootstrap(config.clone()); + if let Some(c) = client { + TOR_STATE.client_config.write().replace((c, config)); + } + break; } } - } + }; + // Wait 5s after launch. + thread::sleep(Duration::from_millis(5000)); TOR_STATE.client_launching.store(false, Ordering::Relaxed); } @@ -362,8 +384,8 @@ impl Tor { // Save failed services if client was not created. if Self::client_config().is_none() { for id in service_ids { - let mut w_services = TOR_STATE.fail.write(); - w_services.insert(id); + TOR_STATE.start.write().remove(&id); + TOR_STATE.fail.write().insert(id); } return; } diff --git a/src/tor/types.rs b/src/tor/types.rs index e7a5e031..b1bda024 100644 --- a/src/tor/types.rs +++ b/src/tor/types.rs @@ -63,7 +63,7 @@ impl TorBridge { pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_2: &'static str = "webtunnel [2001:db8:eedb:cae7:a345:4f72:f9cc:5de0]:443 B3C81E7A0CA474270DAA4A2C8633E1CA8935C37D url=https://wordpress.far-east-investment.ru/sORes7268CEUSRD7hAWvJU5A ver=0.0.3"; pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_3: &'static str = "webtunnel [2001:db8:945c:e0b9:7e4c:c974:ff00:d4c5]:443 91937F3EFB3BE5169788AC7C8BF07460B7E306DB url=https://kabel.entreri.de/YXbp1dNrJeOF8giAFFYWxvmf ver=0.0.3"; pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_4: &'static str = "webtunnel [2001:db8:4767:7aa2:df21:1b2b:d7f9:caee]:443 CD193CF0D0C29551928C01FCB28D1200D9F27CFA url=https://occurrence.pics/68SzSlQCRgnfSo32eLyjC1V3 ver=0.0.3"; - pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_5: &'static str = "webtunnel [2001:db8:4c9a:ffe8:70f8:d5af:a8f1:cf56]:443 DA1ECF055635C1A6ED7F5B5F36296A5E3015CE57 url=https://1axfa6xb.xoomlia.com/6qtxxkjw/ ver=0.0.3"; + pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_5: &'static str = "webtunnel [2001:db8:ce90:3593:272e:4975:a031:55b]:443 12382A2F3912AD1983A97C8709CBAE47ADB60BE3 url=https://miranda.today/LWwxIXDHCyyScn7oDauPMTmX ver=0.0.3"; pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_6: &'static str = "webtunnel [2001:db8:a12b:ff8:8a1a:a05b:5f21:2ccc]:443 F2A9C5AEE0A420EB9D55F9497B3C0FA243A2A770 url=https://bridge.lovecloud.me/wss-wc3p0euqrlne98t9 ver=0.0.3"; pub const ADDITIONAL_WEBTUNNEL_CONN_LINE_7: &'static str = "webtunnel [2001:db8:8ed6:e6c9:5fc9:9f20:a373:2374]:443 1636A2EFFBAA4B162F5FF461A1663EB55C41AE11 url=https://hanoi.delivery/roQFPLtlspWT6yIKeXD6lEci ver=0.0.3";