2024-04-25 15:15:00 +03:00
// 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.
2024-04-30 18:15:03 +03:00
use std ::collections ::{ BTreeMap , BTreeSet } ;
use std ::net ::{ IpAddr , Ipv4Addr , SocketAddr } ;
2024-05-15 17:36:09 +03:00
use std ::str ::FromStr ;
2024-04-25 15:15:00 +03:00
use std ::sync ::{ Arc , RwLock } ;
2024-05-15 18:05:45 +03:00
use std ::thread ;
2024-05-15 17:36:09 +03:00
use std ::time ::Duration ;
use arti_client ::config ::pt ::TransportConfigBuilder ;
2024-04-25 15:15:00 +03:00
use lazy_static ::lazy_static ;
use futures ::task ::SpawnExt ;
2024-04-27 02:19:40 +03:00
use arti_client ::{ TorClient , TorClientConfig } ;
2024-05-15 17:36:09 +03:00
use arti_client ::config ::{ BridgeConfigBuilder , TorClientConfigBuilder } ;
2024-04-27 02:19:40 +03:00
use fs_mistrust ::Mistrust ;
use grin_util ::secp ::SecretKey ;
use ed25519_dalek ::hazmat ::ExpandedSecretKey ;
use curve25519_dalek ::digest ::Digest ;
use sha2 ::Sha512 ;
2024-05-15 17:36:09 +03:00
use tokio ::time ::{ sleep , sleep_until } ;
use tor_config ::CfgPath ;
2024-04-27 03:01:29 +03:00
use tor_rtcompat ::tokio ::TokioNativeTlsRuntime ;
2024-05-15 20:27:58 +03:00
use tor_rtcompat ::{ BlockOn , PreferredRuntime , Runtime } ;
2024-04-27 02:19:40 +03:00
use tor_hsrproxy ::OnionServiceReverseProxy ;
use tor_hsrproxy ::config ::{ Encapsulation , ProxyAction , ProxyPattern , ProxyRule , TargetAddr , ProxyConfigBuilder } ;
use tor_hsservice ::config ::OnionServiceConfigBuilder ;
2024-04-30 18:15:03 +03:00
use tor_hsservice ::{ HsIdKeypairSpecifier , HsIdPublicKeySpecifier , HsNickname , RunningOnionService } ;
2024-04-27 02:19:40 +03:00
use tor_keymgr ::{ ArtiNativeKeystore , KeyMgrBuilder , KeystoreSelector } ;
use tor_llcrypto ::pk ::ed25519 ::ExpandedKeypair ;
use tor_hscrypto ::pk ::{ HsIdKey , HsIdKeypair } ;
2024-04-30 18:15:03 +03:00
use arti_hyper ::ArtiHttpConnector ;
2024-05-15 17:36:09 +03:00
use hyper ::{ Body , Uri } ;
2024-04-30 18:15:03 +03:00
use tls_api ::{ TlsConnector as TlsConnectorTrait , TlsConnectorBuilder } ;
2024-04-25 15:15:00 +03:00
2024-04-30 18:15:03 +03:00
// On aarch64-apple-darwin targets there is an issue with the native and rustls
// tls implementation so this makes it fall back to the openssl variant.
//
// https://gitlab.torproject.org/tpo/core/arti/-/issues/715
#[ cfg(not(all(target_vendor = " apple " , target_arch = " aarch64 " ))) ]
use tls_api_native_tls ::TlsConnector ;
#[ cfg(all(target_vendor = " apple " , target_arch = " aarch64 " )) ]
use tls_api_openssl ::TlsConnector ;
use crate ::tor ::TorConfig ;
2024-04-25 15:15:00 +03:00
lazy_static! {
/// Static thread-aware state of [`Node`] to be updated from separate thread.
2024-04-30 18:15:03 +03:00
static ref TOR_SERVER_STATE : Arc < Tor > = Arc ::new ( Tor ::default ( ) ) ;
2024-04-25 15:15:00 +03:00
}
2024-04-27 02:19:40 +03:00
/// Tor server to use as SOCKS proxy for requests and to launch Onion services.
2024-04-30 18:15:03 +03:00
pub struct Tor {
2024-04-27 02:19:40 +03:00
/// Mapping of running Onion services identifiers to proxy.
2024-04-30 18:15:03 +03:00
running_services : Arc < RwLock < BTreeMap < String ,
( Arc < RunningOnionService > , Arc < OnionServiceReverseProxy > ) > > > ,
/// Starting Onion services identifiers.
starting_services : Arc < RwLock < BTreeSet < String > > > ,
/// Failed Onion services identifiers.
2024-05-15 20:27:58 +03:00
failed_services : Arc < RwLock < BTreeSet < String > > > ,
/// Checking Onion services identifiers.
checking_services : Arc < RwLock < BTreeSet < String > > >
2024-04-25 15:15:00 +03:00
}
2024-04-30 18:15:03 +03:00
impl Default for Tor {
2024-04-25 15:15:00 +03:00
fn default ( ) -> Self {
Self {
2024-04-30 18:15:03 +03:00
running_services : Arc ::new ( RwLock ::new ( BTreeMap ::new ( ) ) ) ,
starting_services : Arc ::new ( RwLock ::new ( BTreeSet ::new ( ) ) ) ,
2024-05-15 20:27:58 +03:00
failed_services : Arc ::new ( RwLock ::new ( BTreeSet ::new ( ) ) ) ,
checking_services : Arc ::new ( RwLock ::new ( BTreeSet ::new ( ) ) )
2024-04-25 15:15:00 +03:00
}
}
}
2024-04-30 18:15:03 +03:00
impl Tor {
2024-05-15 17:36:09 +03:00
async fn build_client ( runtime : TokioNativeTlsRuntime )
2024-05-15 20:27:58 +03:00
-> ( TorClient < TokioNativeTlsRuntime > , TorClientConfig ) {
2024-05-15 17:36:09 +03:00
// 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 )
2024-05-15 20:27:58 +03:00
. config ( config . clone ( ) )
. create_bootstrapped ( )
. await
. unwrap ( ) , config )
2024-05-15 17:36:09 +03:00
}
2024-04-30 18:15:03 +03:00
/// Send post request using Tor.
2024-05-15 20:27:58 +03:00
pub async fn post ( body : String , url : String , runtime : TokioNativeTlsRuntime ) -> Option < String > {
2024-05-15 17:36:09 +03:00
// Create client.
let ( client , _ ) = Self ::build_client ( runtime ) . await ;
2024-04-30 18:15:03 +03:00
// Create http tor-powered client to post data.
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 ) ;
// Create request.
let req = hyper ::Request ::builder ( )
. method ( hyper ::Method ::POST )
. uri ( url )
. body ( Body ::from ( body ) )
. unwrap ( ) ;
// Send request.
let mut resp = None ;
match http . request ( req ) . await {
Ok ( r ) = > {
match hyper ::body ::to_bytes ( r ) . await {
Ok ( raw ) = > {
resp = Some ( String ::from_utf8_lossy ( & raw ) . to_string ( ) )
} ,
Err ( _ ) = > { } ,
2024-04-25 15:15:00 +03:00
}
2024-04-30 18:15:03 +03:00
} ,
Err ( _ ) = > { } ,
}
resp
2024-04-25 15:15:00 +03:00
}
2024-04-30 18:15:03 +03:00
/// Check if Onion service is starting.
pub fn is_service_starting ( id : & String ) -> bool {
let r_services = TOR_SERVER_STATE . starting_services . read ( ) . unwrap ( ) ;
r_services . contains ( id )
2024-04-25 15:15:00 +03:00
}
2024-04-27 02:19:40 +03:00
/// Check if Onion service is running.
pub fn is_service_running ( id : & String ) -> bool {
let r_services = TOR_SERVER_STATE . running_services . read ( ) . unwrap ( ) ;
r_services . contains_key ( id )
}
2024-04-30 18:15:03 +03:00
/// Check if Onion service failed on start.
pub fn is_service_failed ( id : & String ) -> bool {
let r_services = TOR_SERVER_STATE . failed_services . read ( ) . unwrap ( ) ;
r_services . contains ( id )
}
2024-05-15 20:27:58 +03:00
/// Check if Onion service is checking.
pub fn is_service_checking ( id : & String ) -> bool {
let r_services = TOR_SERVER_STATE . checking_services . read ( ) . unwrap ( ) ;
r_services . contains ( id )
}
2024-04-27 02:19:40 +03:00
/// Stop running Onion service.
pub fn stop_service ( id : & String ) {
let mut w_services = TOR_SERVER_STATE . running_services . write ( ) . unwrap ( ) ;
2024-04-30 18:15:03 +03:00
if let Some ( ( svc , proxy ) ) = w_services . remove ( id ) {
2024-04-27 02:19:40 +03:00
proxy . shutdown ( ) ;
2024-04-30 18:15:03 +03:00
drop ( svc ) ;
2024-04-27 02:19:40 +03:00
}
}
2024-04-30 18:15:03 +03:00
/// Start Onion service from listening local port and [`SecretKey`].
pub fn start_service ( port : u16 , key : SecretKey , id : & String ) {
2024-04-27 02:19:40 +03:00
// Check if service is already running.
if Self ::is_service_running ( id ) {
return ;
2024-04-30 18:15:03 +03:00
} else {
// Save starting service.
let mut w_services = TOR_SERVER_STATE . starting_services . write ( ) . unwrap ( ) ;
w_services . insert ( id . clone ( ) ) ;
// Remove service from failed.
let mut w_services = TOR_SERVER_STATE . failed_services . write ( ) . unwrap ( ) ;
w_services . remove ( id ) ;
2024-04-27 02:19:40 +03:00
}
2024-04-30 18:15:03 +03:00
let service_id = id . clone ( ) ;
2024-05-15 20:27:58 +03:00
thread ::spawn ( move | | {
let runtime = TokioNativeTlsRuntime ::create ( ) . unwrap ( ) ;
let runtime_client = runtime . clone ( ) ;
runtime . spawn ( async move {
let ( client , config ) = Self ::build_client ( runtime_client . clone ( ) ) . await ;
// Add service key to keystore.
let hs_nickname = HsNickname ::new ( service_id . clone ( ) ) . unwrap ( ) ;
Self ::add_service_key ( config . fs_mistrust ( ) , & key , & hs_nickname ) ;
// Launch Onion service.
let service_config = OnionServiceConfigBuilder ::default ( )
. nickname ( hs_nickname . clone ( ) )
. build ( )
. unwrap ( ) ;
let ( service , request ) = client . launch_onion_service ( service_config ) . unwrap ( ) ;
// Launch service proxy.
let addr = SocketAddr ::new ( IpAddr ::from ( Ipv4Addr ::LOCALHOST ) , port ) ;
tokio ::spawn (
Self ::run_service_proxy ( addr , client , service . clone ( ) , request , hs_nickname . clone ( ) )
) . await . unwrap ( ) ;
// Check service availability if not checking.
if Self ::is_service_checking ( & service_id ) {
return ;
}
let url = format! ( " http:// {} /v2/foreign " , service . onion_name ( ) . unwrap ( ) . to_string ( ) ) ;
thread ::spawn ( move | | {
let runtime = TokioNativeTlsRuntime ::create ( ) . unwrap ( ) ;
let client_runtime = runtime . clone ( ) ;
// Put service to checking.
{
let mut w_services = TOR_SERVER_STATE . checking_services . write ( ) . unwrap ( ) ;
w_services . insert ( service_id . clone ( ) ) ;
2024-05-15 17:36:09 +03:00
}
2024-05-15 20:27:58 +03:00
runtime
. spawn ( async move {
loop {
// Create client.
let ( client , _ ) = Self ::build_client ( client_runtime . 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 ) ;
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 ) ;
} ,
Err ( e ) = > {
// Put service to starting.
let mut w_services = TOR_SERVER_STATE . starting_services . write ( ) . unwrap ( ) ;
w_services . insert ( service_id . clone ( ) ) ;
} ,
}
if ! Self ::is_service_running ( & service_id ) & &
! Self ::is_service_starting ( & service_id ) {
// Remove service from checking.
let mut w_services = TOR_SERVER_STATE . checking_services . write ( ) . unwrap ( ) ;
w_services . remove ( & service_id ) ;
break ;
}
}
} ) . unwrap ( ) ;
} ) ;
} ) . unwrap ( ) ;
} ) ;
2024-04-30 18:15:03 +03:00
}
2024-04-27 02:19:40 +03:00
2024-04-30 18:15:03 +03:00
/// Launch Onion service proxy.
async fn run_service_proxy < R , S > (
addr : SocketAddr ,
client : TorClient < R > ,
service : Arc < RunningOnionService > ,
request : S ,
nickname : HsNickname
)
where
R : Runtime ,
S : futures ::Stream < Item = tor_hsservice ::RendRequest > + Unpin + Send + 'static ,
{
let id = nickname . to_string ( ) ;
let runtime = client . runtime ( ) . clone ( ) ;
2024-04-27 02:19:40 +03:00
// Setup proxy to forward request from Tor address to local address.
let proxy_rule = ProxyRule ::new (
ProxyPattern ::one_port ( 80 ) . unwrap ( ) ,
ProxyAction ::Forward ( Encapsulation ::Simple , TargetAddr ::Inet ( addr ) ) ,
) ;
let mut proxy_cfg_builder = ProxyConfigBuilder ::default ( ) ;
proxy_cfg_builder . set_proxy_ports ( vec! [ proxy_rule ] ) ;
let proxy = OnionServiceReverseProxy ::new ( proxy_cfg_builder . build ( ) . unwrap ( ) ) ;
2024-04-30 18:15:03 +03:00
// Save running service.
let mut w_services = TOR_SERVER_STATE . running_services . write ( ) . unwrap ( ) ;
w_services . insert ( id . clone ( ) , ( service . clone ( ) , proxy . clone ( ) ) ) ;
// Start proxy for launched service.
2024-04-27 02:19:40 +03:00
client
. runtime ( )
. spawn ( async move {
2024-04-30 18:15:03 +03:00
match proxy
. handle_requests ( runtime , nickname . clone ( ) , request )
. await {
2024-04-27 02:19:40 +03:00
Ok ( ( ) ) = > {
2024-04-30 18:15:03 +03:00
// Remove service from running.
let mut w_services =
TOR_SERVER_STATE . running_services . write ( ) . unwrap ( ) ;
w_services . remove ( & id ) ;
2024-04-27 02:19:40 +03:00
}
Err ( e ) = > {
2024-04-30 18:15:03 +03:00
// Remove service from running.
let mut w_services =
TOR_SERVER_STATE . running_services . write ( ) . unwrap ( ) ;
w_services . remove ( & id ) ;
// Save failed service.
let mut w_services =
TOR_SERVER_STATE . failed_services . write ( ) . unwrap ( ) ;
w_services . insert ( id ) ;
2024-04-27 02:19:40 +03:00
}
}
} ) . unwrap ( ) ;
}
2024-04-30 18:15:03 +03:00
/// Save Onion service key to keystore.
2024-04-27 02:19:40 +03:00
fn add_service_key ( mistrust : & Mistrust , key : & SecretKey , hs_nickname : & HsNickname ) {
let arti_store =
2024-04-30 18:15:03 +03:00
ArtiNativeKeystore ::from_path_and_mistrust ( TorConfig ::keystore_path ( ) , & mistrust )
2024-04-27 02:19:40 +03:00
. unwrap ( ) ;
let key_manager = KeyMgrBuilder ::default ( )
. default_store ( Box ::new ( arti_store ) )
. build ( )
. unwrap ( ) ;
let expanded_sk = ExpandedSecretKey ::from_bytes (
Sha512 ::default ( )
. chain_update ( key )
. finalize ( )
. as_ref ( ) ,
) ;
let mut sk_bytes = [ 0_ u8 ; 64 ] ;
sk_bytes [ 0 . . 32 ] . copy_from_slice ( & expanded_sk . scalar . to_bytes ( ) ) ;
sk_bytes [ 32 .. 64 ] . copy_from_slice ( & expanded_sk . hash_prefix ) ;
let expanded_kp = ExpandedKeypair ::from_secret_key_bytes ( sk_bytes ) . unwrap ( ) ;
key_manager
. insert (
HsIdKey ::from ( expanded_kp . public ( ) . clone ( ) ) ,
& HsIdPublicKeySpecifier ::new ( hs_nickname . clone ( ) ) ,
KeystoreSelector ::Default ,
)
. unwrap ( ) ;
key_manager
. insert (
HsIdKeypair ::from ( expanded_kp ) ,
& HsIdKeypairSpecifier ::new ( hs_nickname . clone ( ) ) ,
KeystoreSelector ::Default ,
)
. unwrap ( ) ;
}
2024-05-15 17:36:09 +03:00
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 ) ;
}
2024-04-25 15:15:00 +03:00
}