/kmp-tor

Kotlin Multiplatform Library for embedding Tor into your applications

Primary LanguageKotlinApache License 2.0Apache-2.0

kmp-tor

Kotlin Kotlin Coroutines Kotlin Atomicfu GitHub license

android jvm

Kotlin Multiplatform support for embedding Tor into your application.

Get Started

Add dependency

// build.gradle.kts
dependencies {
    val vTor = "0.4.7.8"
    val vKmpTor = "1.0.0"
    implementation("io.matthewnelson.kotlin-components:kmp-tor:$vTor+$vKmpTor")
}
// build.gradle
dependencies {
   def vTor = '0.4.7.8'
   def vKmpTor = '1.0.0'
   implementation "io.matthewnelson.kotlin-components:kmp-tor:$vTor+$vKmpTor"
}
Configuring an Android Project
  • See the Android section of Configuring Gradle to setup things up so the Tor binaries are properly extracted upon app install.

  • By default, TorService needs no configuration and runs in the background. For configuring it to run as a Foreground service, see the following:

  • See the Sample App for a basic setup of TorManager and your TorConfig.
Configuring a Java Project
  • See the JavaFX Sample App Gradle Configuration for a basic gradle/dependency configuration.
  • See the JavaFx Sample App for a basic setup example.
  • Run the JavaFx Sample via ./gradlew :samples:kotlin:javafx:run -PKMP_TARGETS=JVM from terminal or cmd prompt.
    • Note: Be sure to run git submodule update --init if you haven't yet so git submodules are initialized.

Extensions

Unix Domain Sockets

Tor supports use of unix domain sockets on Linux (and Android) for the following:

  • ControlPort
  • SocksPort
  • HiddenServicePort

How to enable unix domain socket support for the ControlPort:

  • For Android, nothing is needed.
  • For JVM, you will need to add the following dependency to your Linux distributions:
// build.gradlew.kts
dependencies {
    val vTor = "0.4.7.8"
    val vKmpTor = "1.0.0"
    
    implementation("io.matthewnelson.kotlin-components:kmp-tor:$vTor+$vKmpTor")
    
    if (isLinuxBuild) {
        // Add the Unix Domain Socket support extension
        implementation("io.matthewnelson.kotlin-components:kmp-tor-ext-unix-socket:$vKmpTor")
    }
}

See the JavaFX Sample App Gradle Configuration dependencies block for more info.

If neither TorConfig.Setting.Ports.Control or TorConfig.Setting.UnixSockets.Control are expressed in your config, TorConfig.Setting.UnixSockets.Control will always be the preferred setting for establishing a connection to Tor's control port, if support is had (JVM + Linux + extension, or on Android). To override this behavior, you can express the TorConfig.Setting.Ports.Control setting when providing your config at startup.

How to enable unix domain socket support for the SocksPort and HiddenServicePort settings:

  • Be running on Linux (or Android)
Callbacks (non-kotlin consumers)
  • For Java projects (who can't use coroutines), you can "wrap" TorManager in an implementation that uses callbacks (ie. CallbackTorManager).
// build.gradle
dependencies {
    def vTor = '0.4.7.8'
    def vKmpTor = '1.0.0'

    implementation "io.matthewnelson.kotlin-components:kmp-tor:$vTor+$vKmpTor"
    // Add the callback extension
    implementation "io.matthewnelson.kotlin-components:kmp-tor-ext-callback-manager:$vKmpTor"

    // You will also need to add the Kotlin Gradle Plugin, and Coroutines dependency.
    
    // If not Android, you will also need to import the binaries for the platforms you wish to
    // support.
}
// Wrapping TorManager instance in its Callback instance (Java)
public class Example1 {
    
    // ..
    TorManager instance = TorManager.newInstance(/* ... */);

    // Wrap that mug...
    CallbackTorManager torManager = new CallbackTorManager(
        instance,
        uncaughtException -> {
            Log.e("MyJavaApp", "Some TorCallback isn't handling an exception...", uncaughtException);
        }
     );
}
  • All requests use coroutines under the hood and are Main thread safe. Results will be dispatched to the supplied callback on the Main thread.
// Multiple callbacks of different styles (Java)
public class Example2 {
    
    // ...
    Task startTask = torManager.start(
        t -> Log.e(TAG, "Failed to start Tor", t),
        startSuccess -> {

            Log.d(TAG, "Tor started successfully");

            Task restartTask = torManager.restart(
                null, // fail silently by omitting failure callback
                (TorCallback<Object>) restartSuccess -> {

                    Log.d(TAG, "Tor restarted successfully");

                    Task restartTask2 = torManager.restart(
                        // Use the provided instance that will automatically throw
                        // the exception, which will pipe it to the handler.
                        TorCallback.THROW,

                        new TorCallback<Object>() {
                            @Override
                            public void invoke(Object o) {
                                Log.d(TAG, "Tor restarted successfully");
                            }
                        }
                    );
                }
            );
        }
    );
}

Git

This project utilizes git submodules. You will need to initialize them when cloning the repository via:

$ git checkout master
$ git pull
$ git submodule update --init

In order to keep submodules updated when pulling the latest code, run:

$ git pull --recurse-submodules