Skip to content

Commit

Permalink
Add restart function
Browse files Browse the repository at this point in the history
  • Loading branch information
icota committed Jan 29, 2024
1 parent b31e4db commit 4d70dab
Show file tree
Hide file tree
Showing 9 changed files with 702 additions and 85 deletions.
632 changes: 592 additions & 40 deletions lib/generated_bindings.dart

Large diffs are not rendered by default.

41 changes: 25 additions & 16 deletions lib/tor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'dart:math';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tor/generated_bindings.dart';
import 'package:tor/generated_bindings.dart' as rust;

DynamicLibrary load(name) {
if (Platform.isAndroid || Platform.isLinux) {
Expand Down Expand Up @@ -40,6 +40,7 @@ class Tor {
static late DynamicLibrary _lib;

Pointer<Void> _clientPtr = nullptr;
Pointer<Void> _proxyPtr = nullptr;

/// Flag to indicate that Tor proxy has started. Traffic is routed through it only if it is also [enabled].
bool get started => _started;
Expand Down Expand Up @@ -162,27 +163,27 @@ class Tor {
int newPort = await _getRandomUnusedPort();

// Start the Tor service in an isolate.
int ptr = await Isolate.run(() async {
final tor = await Isolate.run(() async {
// Load the Tor library.
var lib = NativeLibrary(load(libName));
var lib = rust.NativeLibrary(load(libName));

// Start the Tor service.
final ptr = lib.tor_start(
final tor = lib.tor_start(
newPort,
stateDir.path.toNativeUtf8() as Pointer<Char>,
cacheDir.path.toNativeUtf8() as Pointer<Char>);

// Throw an exception if the Tor service fails to start.
if (ptr == nullptr) {
if (tor.client == nullptr) {
throwRustException(lib);
}

// Return the pointer.
return ptr.address;
return tor;
});

// Set the client pointer and started flag.
_clientPtr = Pointer.fromAddress(ptr);
_clientPtr = Pointer.fromAddress(tor.client.address);
_proxyPtr = Pointer.fromAddress(tor.proxy.address);
_started = true;

// Bootstrap the Tor service.
Expand All @@ -205,10 +206,10 @@ class Tor {
/// Returns void.
void bootstrap() {
// Load the Tor library.
final lib = NativeLibrary(_lib);
final lib = rust.NativeLibrary(_lib);

// Bootstrap the Tor service.
_bootstrapped = lib.tor_bootstrap(_clientPtr);
_bootstrapped = lib.tor_client_bootstrap(_clientPtr);

// Throw an exception if the Tor service fails to bootstrap.
if (!bootstrapped) {
Expand All @@ -222,10 +223,18 @@ class Tor {
broadcastState();
}

void restart() {
// TODO: arti seems to recover by itself and there is no client restart fn
// TODO: but follow up with them if restart is truly unnecessary
// if (enabled && started && circuitEstablished) {}
/// Restart the proxy on a new port
void restart() async {
// Load the Tor library.
final lib = rust.NativeLibrary(_lib);
final Pointer<rust.Tor> tor = calloc<rust.Tor>();

tor.ref.proxy = _proxyPtr;
tor.ref.client = _clientPtr;

final newPort = await _getRandomUnusedPort();
_proxyPtr = lib.tor_proxy_restart(tor.ref, newPort).proxy;
_proxyPort = newPort;
}

Future<void> isReady() async {
Expand All @@ -247,7 +256,7 @@ class Tor {
}));
}

static throwRustException(NativeLibrary lib) {
static throwRustException(rust.NativeLibrary lib) {
String rustError = lib.tor_last_error_message().cast<Utf8>().toDartString();

throw _getRustException(rustError);
Expand All @@ -262,6 +271,6 @@ class Tor {
}

void hello() {
NativeLibrary(_lib).tor_hello();
rust.NativeLibrary(_lib).tor_hello();
}
}
6 changes: 3 additions & 3 deletions lib/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

import 'package:tor/generated_bindings.dart';
import 'package:tor/generated_bindings.dart' as rust;
import 'package:tor/tor.dart';

int getNofileLimit() {
return NativeLibrary(load(Tor.libName)).tor_get_nofile_limit();
return rust.NativeLibrary(load(Tor.libName)).tor_get_nofile_limit();
}

int setNofileLimit(int limit) {
return NativeLibrary(load(Tor.libName)).tor_set_nofile_limit(limit);
return rust.NativeLibrary(load(Tor.libName)).tor_set_nofile_limit(limit);
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: tor
description: A multi-platform Flutter plugin for managing a Tor proxy. Based on arti.
version: 0.0.2
version: 0.0.3
homepage: https://github.com/Foundation-Devices/tor

platforms:
android:
ios:
linux:
macos:
windows: # Not tested but it should work
windows:

environment:
sdk: '>=3.0.0 <4.0.0'
Expand Down
7 changes: 4 additions & 3 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[package]
name = "tor"
version = "0.1.0"
version = "0.0.3"
authors = ["Igor Cota <[email protected]>"]
edition = "2021"

Expand All @@ -21,6 +21,7 @@ tor-config = "0.10.0"
log = "0.4.20"
#android_log-sys = "0.3.1"
rlimit = "0.10.1"
anyhow = "1.0.79"

[build-dependencies]
cbindgen = "= 0.24.3"
4 changes: 2 additions & 2 deletions rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ fn main() {
..Default::default()
};

cbindgen::generate_with_config(&crate_dir, config)
cbindgen::generate_with_config(crate_dir, config)
.unwrap()
.write_to_file(&output_file);
.write_to_file(output_file);
}

fn target_dir() -> PathBuf {
Expand Down
77 changes: 61 additions & 16 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use lazy_static::lazy_static;
use std::ffi::{c_char, c_void, CStr};
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::BlockOn;
Expand All @@ -28,13 +29,22 @@ 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,
}

#[no_mangle]
pub unsafe extern "C" fn tor_start(
socks_port: u16,
state_dir: *const c_char,
cache_dir: *const c_char,
) -> *mut c_void {
let err_ret = ptr::null_mut();
) -> Tor {
let err_ret = Tor {
client: ptr::null_mut(),
proxy: ptr::null_mut(),
};

let state_dir = unwrap_or_return!(CStr::from_ptr(state_dir).to_str(), err_ret);
let cache_dir = unwrap_or_return!(CStr::from_ptr(cache_dir).to_str(), err_ret);
Expand All @@ -60,25 +70,19 @@ pub unsafe extern "C" fn tor_start(
err_ret
);

let client_clone = client.clone();

println!("Starting proxy!");
let rt = RUNTIME.as_ref().unwrap();
let handle = rt.spawn(socks::run_socks_proxy(
runtime.clone(),
client_clone,
Listen::new_localhost(socks_port),
));
//let client_clone = client.clone();

let handle_box = Box::new(handle);
Box::leak(handle_box);
let proxy_handle_box = Box::new(start_proxy(socks_port, client.clone()));
let client_box = Box::new(client.clone());

let client_box = Box::new(client);
Box::into_raw(client_box) as *mut c_void
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_bootstrap(client: *mut c_void) -> bool {
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>)
Expand All @@ -88,6 +92,47 @@ pub unsafe extern "C" fn tor_bootstrap(client: *mut c_void) -> bool {
true
}

#[no_mangle]
pub unsafe extern "C" fn tor_proxy_stop(proxy: *mut c_void) -> bool {
let proxy = {
assert!(!proxy.is_null());
Box::from_raw(proxy as *mut JoinHandle<anyhow::Result<()>>)
};

proxy.abort();
true
}

#[no_mangle]
pub unsafe extern "C" fn tor_proxy_restart(tor: Tor, port: u16) -> Tor {
tor_proxy_stop(tor.proxy);

let client_box = {
assert!(!tor.client.is_null());
Box::from_raw(tor.client as *mut TorClient<TokioNativeTlsRuntime>)
};

let proxy_box = Box::new(start_proxy(port, *client_box.clone()));

Tor {
client: Box::into_raw(client_box) as *mut c_void,
proxy: Box::into_raw(proxy_box) as *mut c_void,
}
}

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),
))
}

// Due to its simple signature this dummy function is the one added (unused) to iOS swift codebase to force Xcode to link the lib
#[no_mangle]
pub unsafe extern "C" fn tor_hello() {
Expand Down
13 changes: 11 additions & 2 deletions rust/target/tor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
#include <stdint.h>
#include <stdlib.h>

void *tor_start(uint16_t socks_port, const char *state_dir, const char *cache_dir);
typedef struct Tor {
void *client;
void *proxy;
} Tor;

bool tor_bootstrap(void *client);
struct Tor tor_start(uint16_t socks_port, const char *state_dir, const char *cache_dir);

bool tor_client_bootstrap(void *client);

bool tor_proxy_stop(void *proxy);

struct Tor tor_proxy_restart(struct Tor tor, uint16_t port);

void tor_hello(void);

Expand Down

0 comments on commit 4d70dab

Please sign in to comment.