261 lines
6.7 KiB
Rust
261 lines
6.7 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::socks;
|
|
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;
|
|
|
|
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(true);
|
|
|
|
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 {
|
|
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(socks::run_socks_proxy(
|
|
client.runtime().clone(),
|
|
client.clone(),
|
|
Listen::new_localhost(port),
|
|
None,
|
|
))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn tor_hello() {
|
|
println!("HELLO THERE");
|
|
}
|