From 099d054289a3a767d3cc0ac90741558d17edd8e3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 13 May 2022 23:45:05 +0200 Subject: [PATCH 1/5] draft how to start arti as a proxy --- common/Cargo.toml | 7 ++++-- common/src/android.rs | 28 ++++++++++++++++++++++++ common/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 921e390..d6f3ed6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -6,13 +6,16 @@ edition = "2018" [dependencies] # use fixed commits because Arti does not have a stable Rust API yet -arti-client = { version = "0.1", features=["static"]} -tor-rtcompat = "0.1" +arti-client = { version = "0.3.0", features=["static"]} +arti = { version = "0.3.0", features=["static"]} +tor-rtcompat = "0.3.0" # helpers anyhow = "1.0.52" futures = "0.3.19" +tracing = "0.1.18" + # used to forward logs to system logger tracing-subscriber = "0.3.3" diff --git a/common/src/android.rs b/common/src/android.rs index f54c0cc..894a1d7 100644 --- a/common/src/android.rs +++ b/common/src/android.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] use crate::run_arti; +use crate::run_arti_proxy; use anyhow::Result; @@ -12,6 +13,33 @@ use jni::objects::{JClass, JString}; use jni::sys::jstring; use jni::JNIEnv; + +#[no_mangle] +pub extern "C" fn Java_net_example_MyClass_runProxy( + env: JNIEnv, + _: JClass, + cache_dir: JString, +) -> jstring { + // if logger initialization failed, there isn't much we can do, not even log it. + // it shouldn't stop Arti from functionning however! + let _ = init_logger(); + + let result = match run_arti_proxy( + &env.get_string(cache_dir) + .expect("cache_dir is invalid") + .to_string_lossy(), + ) { + Ok(_res) => ("Proxy stopped. Good bye.".to_string()), + Err(e) => format!("Error: {}", e), + }; + + let output = env + .new_string(result) + .expect("failed to create java string"); + + output.into_inner() +} + /// Create a static method myMethod on class net.example.MyClass #[no_mangle] pub extern "C" fn Java_net_example_MyClass_myMethod( diff --git a/common/src/lib.rs b/common/src/lib.rs index 6405886..2056425 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,8 +1,15 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use futures::{AsyncReadExt, AsyncWriteExt}; -use arti_client::{config::TorClientConfigBuilder, TorClient}; +use arti_client::{config::TorClientConfigBuilder, TorClient, BootstrapBehavior::OnDemand}; +use arti::{socks}; use tor_rtcompat::BlockOn; +use futures::FutureExt; +use tracing::{info}; + + +/// Shorthand for a boxed and pinned Future. +type PinnedFuture = std::pin::Pin>>; fn run_arti(to: &str, cache: &str) -> Result { let runtime = tor_rtcompat::PreferredRuntime::create()?; @@ -33,6 +40,46 @@ fn run_arti(to: &str, cache: &str) -> Result { }) } +fn run_arti_proxy(cache: &str) -> Result<(), anyhow::Error> { + let runtime = tor_rtcompat::PreferredRuntime::create()?; + let config = TorClientConfigBuilder::from_directories( + format!("{}/arti-data", cache), + format!("{}/arti-cache", cache), + ).build()?; + // Using OnDemand arranges that, while we are bootstrapping, incoming connections wait + // for bootstrap to complete, rather than getting errors. + let rt_copy = runtime.clone(); + + rt_copy.block_on(async { + let client_builder = TorClient::with_runtime(runtime.clone()) + .config(config) + .bootstrap_behavior(OnDemand); + + let client = client_builder.create_unbootstrapped()?; + + let mut proxy: Vec, &str)>> = Vec::new(); + let runtime = runtime.clone(); + let client_2 = client.isolated_client(); + proxy.push(Box::pin(async move { + let res = socks::run_socks_proxy(runtime, client_2, 9150).await; + (res, "SOCKS") + })); + + let proxy = futures::future::select_all(proxy).map(|(finished, _index, _others)| finished); + futures::select!( + + r = proxy.fuse() + => r.0.context(format!("{} proxy failure", r.1)), + r = async { + client.bootstrap().await?; + info!("Sufficiently bootstrapped; system SOCKS now functional."); + futures::future::pending::>().await + }.fuse() + => r.context("bootstrap"), + ) + }) +} + /// Expose the JNI interface for Android #[cfg(target_os = "android")] pub mod android; -- GitLab From 49711b07d89565f8d6e1135a64d0c6b4a6517ba9 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 13 May 2022 23:49:45 +0200 Subject: [PATCH 2/5] implement JNI --- android/app/src/main/java/net/example/MainActivity.java | 3 +++ android/app/src/main/java/net/example/MyClass.java | 1 + 2 files changed, 4 insertions(+) diff --git a/android/app/src/main/java/net/example/MainActivity.java b/android/app/src/main/java/net/example/MainActivity.java index 385b174..97876b1 100644 --- a/android/app/src/main/java/net/example/MainActivity.java +++ b/android/app/src/main/java/net/example/MainActivity.java @@ -12,6 +12,9 @@ public class MainActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + Thread t = new Thread(() -> MyClass.runProxy(getCacheDir().getAbsolutePath())); + t.start(); + TextView text = (TextView) this.findViewById(R.id.textField); String res = MyClass.myMethod("ifconfig.me", getCacheDir().getAbsolutePath()); text.setText(res); diff --git a/android/app/src/main/java/net/example/MyClass.java b/android/app/src/main/java/net/example/MyClass.java index 3630cbc..e7e09c9 100644 --- a/android/app/src/main/java/net/example/MyClass.java +++ b/android/app/src/main/java/net/example/MyClass.java @@ -1,6 +1,7 @@ package net.example; class MyClass { + static native String runProxy(final String storage); static native String myMethod(final String target, final String storage); static { System.loadLibrary("arti_mobile"); -- GitLab From 8c10171886d21e851e17d96ddb7eed8d8ad1bf09 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 23 May 2022 17:53:12 +0200 Subject: [PATCH 3/5] use a different logging lib, expose android logging initialization to JNI --- common/Cargo.toml | 4 ++++ common/src/android.rs | 34 +++++++++++++++++++++------------- common/src/lib.rs | 4 +++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index d6f3ed6..c959eb2 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -33,6 +33,10 @@ jni = { version = "0.19", default-features = false } # dependancies to make logs available with logcat tracing-android = "0.1.6" +# android logging +android_logger = "0.11" +log = "0.4" + # dependancy to help implementing lockf, for android < 7.0 libc = "0.2.112" diff --git a/common/src/android.rs b/common/src/android.rs index 894a1d7..b23c4df 100644 --- a/common/src/android.rs +++ b/common/src/android.rs @@ -1,18 +1,18 @@ #![allow(non_snake_case)] +extern crate android_logger; use crate::run_arti; use crate::run_arti_proxy; -use anyhow::Result; - -use std::sync::Once; -use tracing_subscriber::fmt::Subscriber; -use tracing_subscriber::prelude::*; - +// use anyhow::Result; +// use std::sync::Once; +// use tracing_subscriber::fmt::Subscriber; +// use tracing_subscriber::prelude::*; use jni::objects::{JClass, JString}; use jni::sys::jstring; use jni::JNIEnv; - +use log::Level; +use android_logger::Config; #[no_mangle] pub extern "C" fn Java_net_example_MyClass_runProxy( @@ -20,10 +20,7 @@ pub extern "C" fn Java_net_example_MyClass_runProxy( _: JClass, cache_dir: JString, ) -> jstring { - // if logger initialization failed, there isn't much we can do, not even log it. - // it shouldn't stop Arti from functionning however! - let _ = init_logger(); - + debug!("starting proxy..."); let result = match run_arti_proxy( &env.get_string(cache_dir) .expect("cache_dir is invalid") @@ -40,6 +37,17 @@ pub extern "C" fn Java_net_example_MyClass_runProxy( output.into_inner() } +#[no_mangle] +pub extern "C" fn Java_net_example_MyClass_initLogging( + _env: JNIEnv, + _: JClass) { + android_logger::init_once( + Config::default() + .with_min_level(Level::Trace) + .with_tag("arti-core")); + debug!("logger initialized"); +} + /// Create a static method myMethod on class net.example.MyClass #[no_mangle] pub extern "C" fn Java_net_example_MyClass_myMethod( @@ -50,7 +58,6 @@ pub extern "C" fn Java_net_example_MyClass_myMethod( ) -> jstring { // if logger initialization failed, there isn't much we can do, not even log it. // it shouldn't stop Arti from functionning however! - let _ = init_logger(); let result = match run_arti( &env.get_string(target) @@ -71,7 +78,7 @@ pub extern "C" fn Java_net_example_MyClass_myMethod( output.into_inner() } -static LOGGER: Once = Once::new(); +/* static LOGGER: Once = Once::new(); fn init_logger() -> Result<()> { if LOGGER.is_completed() { @@ -80,6 +87,7 @@ fn init_logger() -> Result<()> { } Ok(()) } +*/ /// Android 5.0 to 6.0 misses this function, which prevent Arti from running. This is a translation /// to Rust of Musl implementation. If you don't plan to support anything below Android 7.0, you diff --git a/common/src/lib.rs b/common/src/lib.rs index 2056425..c213a20 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,3 +1,5 @@ +#[macro_use] extern crate log; + use anyhow::{Context, Result}; use futures::{AsyncReadExt, AsyncWriteExt}; @@ -49,7 +51,7 @@ fn run_arti_proxy(cache: &str) -> Result<(), anyhow::Error> { // Using OnDemand arranges that, while we are bootstrapping, incoming connections wait // for bootstrap to complete, rather than getting errors. let rt_copy = runtime.clone(); - + debug!("run_arti_proxy"); rt_copy.block_on(async { let client_builder = TorClient::with_runtime(runtime.clone()) .config(config) -- GitLab From 64969bc3d6114ebbcb2516792b338394a42e5091 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 23 May 2022 17:59:03 +0200 Subject: [PATCH 4/5] very basic arti proxy example using okhttp client with socks5 proxy configuration --- android/app/build.gradle | 2 +- .../main/java/net/example/MainActivity.java | 60 +++++++++++++++++-- .../src/main/java/net/example/MyClass.java | 1 + 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index fa5f689..d78a320 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -28,7 +28,7 @@ android { } dependencies { - + implementation 'com.squareup.okhttp3:okhttp:3.12.12' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' diff --git a/android/app/src/main/java/net/example/MainActivity.java b/android/app/src/main/java/net/example/MainActivity.java index 97876b1..411db1b 100644 --- a/android/app/src/main/java/net/example/MainActivity.java +++ b/android/app/src/main/java/net/example/MainActivity.java @@ -1,22 +1,72 @@ package net.example; -import androidx.appcompat.app.AppCompatActivity; - import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + public class MainActivity extends AppCompatActivity { + private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + MyClass.initLogging(); Thread t = new Thread(() -> MyClass.runProxy(getCacheDir().getAbsolutePath())); t.start(); - TextView text = (TextView) this.findViewById(R.id.textField); - String res = MyClass.myMethod("ifconfig.me", getCacheDir().getAbsolutePath()); - text.setText(res); + final TextView text = (TextView) this.findViewById(R.id.textField); + Handler handler = new Handler(Looper.getMainLooper()); + Thread t2 = new Thread(() -> { + String hostname = "localhost"; + int port = 9150; + Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(hostname, port)); + + OkHttpClient client = new OkHttpClient.Builder() + .proxy(proxy) + .build(); + + Request.Builder requestBuilder = new Request.Builder() + .url("https://wtfismyip.com/json") + .method("GET", null); + Request request = requestBuilder.build(); + + Response response = null; + try { + response = client.newCall(request).execute(); + } catch (IOException e) { + e.printStackTrace(); + } + if (response == null || !response.isSuccessful()) { + Log.e(TAG, "call not successful"); + return; + } + if (response.body() == null) { + Log.e(TAG, "empty body :("); + return; + } + + try { + String body = response.body().string(); + Log.d(TAG, body); + handler.post(() -> text.setText(body)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + t2.start(); } } \ No newline at end of file diff --git a/android/app/src/main/java/net/example/MyClass.java b/android/app/src/main/java/net/example/MyClass.java index e7e09c9..c049ed7 100644 --- a/android/app/src/main/java/net/example/MyClass.java +++ b/android/app/src/main/java/net/example/MyClass.java @@ -3,6 +3,7 @@ package net.example; class MyClass { static native String runProxy(final String storage); static native String myMethod(final String target, final String storage); + static native void initLogging(); static { System.loadLibrary("arti_mobile"); } -- GitLab From 35e56afa9b1a9e0d7ce04cb81b469fdab1907d3f Mon Sep 17 00:00:00 2001 From: ankitgusai Date: Sat, 25 Jun 2022 10:09:49 -0400 Subject: [PATCH 5/5] Proxy close method draft 1 --- android/app/src/main/AndroidManifest.xml | 2 + .../main/java/net/example/MainActivity.java | 17 ++++- .../src/main/java/net/example/MyClass.java | 15 +++- .../app/src/main/res/layout/activity_main.xml | 11 +++ common/src/android.rs | 71 ++++++++++++++++--- common/src/lib.rs | 32 +++++---- 6 files changed, 121 insertions(+), 27 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5950a21..9f6dc26 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + MyClass.runProxy(getCacheDir().getAbsolutePath())); + MyClass myClass = new MyClass(); + + myClass.initLogging(); + Thread t = new Thread(() -> myClass.runProxy(getCacheDir().getAbsolutePath())); t.start(); final TextView text = (TextView) this.findViewById(R.id.textField); Handler handler = new Handler(Looper.getMainLooper()); Thread t2 = new Thread(() -> { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } String hostname = "localhost"; int port = 9150; Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(hostname, port)); @@ -67,6 +76,8 @@ public class MainActivity extends AppCompatActivity { e.printStackTrace(); } }); - t2.start(); + t2.start(); + + findViewById(R.id.button).setOnClickListener(v -> myClass.closeProxy(myClass.exitPointer)); } } \ No newline at end of file diff --git a/android/app/src/main/java/net/example/MyClass.java b/android/app/src/main/java/net/example/MyClass.java index c049ed7..fc8332c 100644 --- a/android/app/src/main/java/net/example/MyClass.java +++ b/android/app/src/main/java/net/example/MyClass.java @@ -1,9 +1,18 @@ package net.example; class MyClass { - static native String runProxy(final String storage); - static native String myMethod(final String target, final String storage); - static native void initLogging(); + + long exitPointer = 0; + + native String runProxy(final String storage); + + native void closeProxy(long exitPointer); + + native String myMethod(final String target, final String storage); + + native void initLogging(); + + static { System.loadLibrary("arti_mobile"); } diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index 6d72f77..1775866 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -16,4 +16,15 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> +