Files
MrbWebLibre/packages/tor/rust/src/lib.rs
T
2025-11-27 08:20:25 +01:00

278 lines
7.2 KiB
Rust

// SPDX-FileCopyrightText: 2023 Foundation Devices Inc.
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
//
// SPDX-License-Identifier: MIT
use crate::error::update_last_error;
use arti_client::config::pt::TransportConfigBuilder;
use arti_client::config::CfgPath;
use arti_client::{DormantMode, TorClient, TorClientConfig};
use lazy_static::lazy_static;
use std::ffi::{c_char, c_void, CStr};
use std::net::SocketAddr;
use std::{io, ptr};
use tokio::runtime::{Builder, Runtime};
use tokio::task::JoinHandle;
use tor_config::Listen;
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
use tor_rtcompat::ToplevelBlockOn;
#[allow(unused_imports)]
use tracing::{debug, error, info, warn};
use tracing_subscriber::fmt::Subscriber;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
// static LOG_FILTER: &str = "info,arti_client=debug,tor_chanmgr=debug,tor_proto=debug";
static LOG_FILTER: &str = "info";
pub use crate::error::tor_last_error_message;
#[cfg(not(target_os = "windows"))]
pub use crate::util::tor_get_nofile_limit;
#[cfg(not(target_os = "windows"))]
pub use crate::util::tor_set_nofile_limit;
#[macro_use]
mod error;
mod util;
lazy_static! {
static ref RUNTIME: io::Result<Runtime> = Builder::new_multi_thread().enable_all().build();
}
#[repr(C)]
pub struct Tor {
client: *mut c_void,
proxy: *mut c_void,
}
struct TorConfig {
state_dir: String,
cache_dir: String,
obfs4_port: u16,
snowflake_port: u16,
bridge_lines: Option<String>,
}
fn build_tor_client_config(
config: &TorConfig,
) -> Result<TorClientConfig, tor_config::ConfigBuildError> {
let mut cfg_builder = TorClientConfig::builder();
cfg_builder
.storage()
.state_dir(CfgPath::new(config.state_dir.clone()))
.cache_dir(CfgPath::new(config.cache_dir.clone()));
cfg_builder.address_filter().allow_onion_addrs(false);
if config.obfs4_port > 0 {
let mut transport = TransportConfigBuilder::default();
transport
.protocols(vec!["obfs4".parse().unwrap()])
.proxy_addr(SocketAddr::new(
"127.0.0.1".parse().unwrap(),
config.obfs4_port,
));
cfg_builder.bridges().transports().push(transport);
}
if config.snowflake_port > 0 {
let mut transport = TransportConfigBuilder::default();
transport
.protocols(vec!["snowflake".parse().unwrap()])
.proxy_addr(SocketAddr::new(
"127.0.0.1".parse().unwrap(),
config.snowflake_port,
));
cfg_builder.bridges().transports().push(transport);
}
if let Some(ref bridge_lines) = config.bridge_lines {
for bridge_line in bridge_lines.split('\n') {
match bridge_line.parse() {
Ok(bridge) => {
cfg_builder.bridges().bridges().push(bridge);
}
Err(e) => {
eprintln!("Failed to parse bridge line '{}': {}", bridge_line, e);
}
}
}
}
cfg_builder.build()
}
unsafe fn parse_config_from_c_params(
state_dir: *const c_char,
cache_dir: *const c_char,
obfs4_port: u16,
snowflake_port: u16,
bridge_lines: *const c_char,
) -> Result<TorConfig, std::str::Utf8Error> {
let state_dir = CStr::from_ptr(state_dir).to_str()?.to_owned();
let cache_dir = CStr::from_ptr(cache_dir).to_str()?.to_owned();
let bridge_lines = if bridge_lines.is_null() {
None
} else {
Some(CStr::from_ptr(bridge_lines).to_str()?.to_owned())
};
Ok(TorConfig {
state_dir,
cache_dir,
obfs4_port,
snowflake_port,
bridge_lines,
})
}
#[no_mangle]
pub unsafe extern "C" fn tor_start(
socks_port: u16,
state_dir: *const c_char,
cache_dir: *const c_char,
obfs4_port: u16,
snowflake_port: u16,
bridge_lines: *const c_char,
) -> Tor {
Subscriber::builder()
.with_env_filter(LOG_FILTER)
.finish()
.with(tracing_android::layer("weblibre_tor").unwrap())
.init();
info!("tor_start");
let err_ret = Tor {
client: ptr::null_mut(),
proxy: ptr::null_mut(),
};
let config = unwrap_or_return!(
parse_config_from_c_params(
state_dir,
cache_dir,
obfs4_port,
snowflake_port,
bridge_lines
),
err_ret
);
let runtime = unwrap_or_return!(TokioNativeTlsRuntime::create(), err_ret);
let cfg = unwrap_or_return!(build_tor_client_config(&config), err_ret);
let client = unwrap_or_return!(
runtime.block_on(async {
TorClient::with_runtime(runtime.clone())
.config(cfg)
.create_bootstrapped()
.await
}),
err_ret
);
let proxy_handle_box = Box::new(start_proxy(socks_port, client.clone()));
let client_box = Box::new(client.clone());
Tor {
client: Box::into_raw(client_box) as *mut c_void,
proxy: Box::into_raw(proxy_handle_box) as *mut c_void,
}
}
#[no_mangle]
pub unsafe extern "C" fn tor_reconfigure(
client: *mut c_void,
state_dir: *const c_char,
cache_dir: *const c_char,
obfs4_port: u16,
snowflake_port: u16,
bridge_lines: *const c_char,
) -> bool {
let config = unwrap_or_return!(
parse_config_from_c_params(
state_dir,
cache_dir,
obfs4_port,
snowflake_port,
bridge_lines
),
false
);
let cfg = unwrap_or_return!(build_tor_client_config(&config), false);
let client = {
assert!(!client.is_null());
Box::from_raw(client as *mut TorClient<TokioNativeTlsRuntime>)
};
unwrap_or_return!(
TorClient::reconfigure(&client, &cfg, tor_config::Reconfigure::WarnOnFailures),
false
);
Box::leak(client);
true
}
#[no_mangle]
pub unsafe extern "C" fn tor_client_bootstrap(client: *mut c_void) -> bool {
let client = {
assert!(!client.is_null());
Box::from_raw(client as *mut TorClient<TokioNativeTlsRuntime>)
};
unwrap_or_return!(client.runtime().block_on(client.bootstrap()), false);
true
}
#[no_mangle]
pub unsafe extern "C" fn tor_client_set_dormant(client: *mut c_void, soft_mode: bool) {
let client = {
assert!(!client.is_null());
Box::from_raw(client as *mut TorClient<TokioNativeTlsRuntime>)
};
let dormant_mode = if soft_mode {
DormantMode::Soft
} else {
DormantMode::Normal
};
client.set_dormant(dormant_mode);
Box::leak(client);
}
#[no_mangle]
pub unsafe extern "C" fn tor_proxy_stop(proxy: *mut c_void) {
let proxy = {
assert!(!proxy.is_null());
Box::from_raw(proxy as *mut JoinHandle<anyhow::Result<()>>)
};
proxy.abort();
}
fn start_proxy(
port: u16,
client: TorClient<TokioNativeTlsRuntime>,
) -> JoinHandle<anyhow::Result<()>> {
println!("Starting proxy!");
let rt = RUNTIME.as_ref().unwrap();
rt.spawn(arti::proxy::run_proxy(
client.runtime().clone(),
client.clone(),
Listen::new_localhost(port),
None,
))
}
#[no_mangle]
pub unsafe extern "C" fn tor_hello() {
println!("HELLO THERE");
}