import tor package

This commit is contained in:
Fabian Freund
2025-09-15 06:54:59 +02:00
parent b3be0b1d7c
commit 1018afc4ae
273 changed files with 18025 additions and 1 deletions
+5861
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
SPDX-License-Identifier: MIT
+28
View File
@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
#
# SPDX-License-Identifier: MIT
[package]
name = "tor"
version = "0.0.9"
authors = ["Igor Cota <igor@foundation.xyz>"]
edition = "2021"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
lazy_static = "1.5"
tokio = { version = "1", features = ["full"] }
arti-client = { version = "0.34.0", features = ["static", "onion-service-client"] }
arti = { version = "1.5.0", features = ["experimental-api", "static"] }
tor-rtcompat = { version = "0.34.0", features = ["static"] }
tor-config = "0.34.0"
log = "0.4.28"
#android_log-sys = "0.3.1"
rlimit = "0.10.2"
anyhow = "1.0.99"
time = "0.3.43"
[build-dependencies]
cbindgen = "=0.24.3"
+36
View File
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2023 Foundation Devices Inc.
// SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
//
// SPDX-License-Identifier: MIT
use cbindgen::{Config, Language};
use std::env;
use std::path::PathBuf;
fn main() {
// Create C header files for Dart
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let package_name = env::var("CARGO_PKG_NAME").unwrap();
let output_file = target_dir()
.join(format!("{}.h", package_name))
.display()
.to_string();
let config = Config {
language: Language::C,
..Default::default()
};
cbindgen::generate_with_config(crate_dir, config)
.unwrap()
.write_to_file(output_file);
}
fn target_dir() -> PathBuf {
if let Ok(target) = env::var("CARGO_TARGET_DIR") {
PathBuf::from(target)
} else {
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("target")
}
}
+6
View File
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
#
# SPDX-License-Identifier: MIT
[toolchain]
channel = "1.87.0"
+3
View File
@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2024 Foundation Devices Inc.
SPDX-License-Identifier: MIT
+61
View File
@@ -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())
}
+260
View File
@@ -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");
}
+18
View File
@@ -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)
}
+37
View File
@@ -0,0 +1,37 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct Tor {
void *client;
void *proxy;
} Tor;
struct Tor tor_start(uint16_t socks_port,
const char *state_dir,
const char *cache_dir,
uint16_t obfs4_port,
uint16_t snowflake_port,
const char *bridge_lines);
bool tor_reconfigure(void *client,
const char *state_dir,
const char *cache_dir,
uint16_t obfs4_port,
uint16_t snowflake_port,
const char *bridge_lines);
bool tor_client_bootstrap(void *client);
void tor_client_set_dormant(void *client, bool soft_mode);
void tor_proxy_stop(void *proxy);
void tor_hello(void);
const char *tor_last_error_message(void);
uint64_t tor_get_nofile_limit(void);
uint64_t tor_set_nofile_limit(uint64_t limit);