import tor package
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2023 Foundation Devices Inc.
|
||||
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use log::{error, warn};
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_char, CString};
|
||||
//pub(crate) use crate::unwrap_or_return;
|
||||
|
||||
thread_local! {
|
||||
static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tor_last_error_message() -> *const c_char {
|
||||
let last_error = match crate::error::take_last_error() {
|
||||
Some(err) => err,
|
||||
None => return CString::new("").unwrap().into_raw(),
|
||||
};
|
||||
|
||||
let error_message = last_error.to_string();
|
||||
CString::new(error_message).unwrap().into_raw()
|
||||
}
|
||||
|
||||
macro_rules! unwrap_or_return {
|
||||
($a:expr,$b:expr) => {
|
||||
match $a {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
update_last_error(e);
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Update the most recent error, clearing whatever may have been there before.
|
||||
pub fn update_last_error<E: Error + 'static>(err: E) {
|
||||
error!("Setting LAST_ERROR: {}", err);
|
||||
|
||||
{
|
||||
// Print a pseudo-backtrace for this error, following back each error's
|
||||
// cause until we reach the root error.
|
||||
let mut cause = err.source();
|
||||
while let Some(parent_err) = cause {
|
||||
warn!("Caused by: {}", parent_err);
|
||||
cause = parent_err.source();
|
||||
}
|
||||
}
|
||||
|
||||
LAST_ERROR.with(|prev| {
|
||||
*prev.borrow_mut() = Some(Box::new(err));
|
||||
});
|
||||
}
|
||||
|
||||
/// Retrieve the most recent error, clearing it in the process.
|
||||
pub fn take_last_error() -> Option<Box<dyn Error>> {
|
||||
LAST_ERROR.with(|prev| prev.borrow_mut().take())
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// 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");
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::error::update_last_error;
|
||||
|
||||
#[no_mangle]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub unsafe extern "C" fn tor_get_nofile_limit() -> u64 {
|
||||
let nofile_limit = unwrap_or_return!(rlimit::getrlimit(rlimit::Resource::NOFILE), 0);
|
||||
nofile_limit.0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub unsafe extern "C" fn tor_set_nofile_limit(limit: u64) -> u64 {
|
||||
unwrap_or_return!(rlimit::increase_nofile_limit(limit), 0)
|
||||
}
|
||||
Reference in New Issue
Block a user