Skip to content

Tor API changes for mobile phone - #367

Open
defnax wants to merge 9 commits into
RetroShare:masterfrom
defnax:torhidden-api
Open

Tor API changes for mobile phone#367
defnax wants to merge 9 commits into
RetroShare:masterfrom
defnax:torhidden-api

Conversation

@defnax

@defnax defnax commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Core/API support for hidden/auto-Tor account creation.

Implemented the libretroshare Tor API changes

Changes include:

  • Added unauthenticated JSON endpoint:
    • /rsTor/setExternalTorConnection
  • Allows configuration before account login/hidden-node initialization.
  • Supports control host/port/password and SOCKS host/port.
  • Skips launching TorProcess for embedded Android Tor or Orbot.
  • Safely handles a missing TorProcess.
  • Never takes ownership of or shuts down externally managed Tor.
  • Reconnects to the configured control port.
  • Uses configured SOCKS details until Tor reports its listener.
  • Treats external Tor as available through isTorAvailable().

Relevant files:

  • rstor.h
  • TorManager.h
  • TorManager.cpp

I also wired rs-mobile to call this endpoint before login or account creation:

  • auth.dart
  • tor_service.dart
image

@defnax defnax changed the title Added required changes to can use on mobile makeHidden Tor API changes for mobile phone Aug 16, 2026
@defnax

defnax commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

needed by this
RetroShare/rs-mobile#42

@defnax

defnax commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@csoler @G10h4ck

@defnax

defnax commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@G10h4ck @csoler

tor binary/packages used from guardianproject
https://github.com/guardianproject/tor-android
https://github.com/RetroShare/rs-mobile/pull/42/changes#diff-9526ccfd1d1813ed49c39f8c54dbeb512607376a007d824b905bc8b4e4d202d9

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter()
        // Guardian Project publishes the same Tor Android runtime used by
        // Orbot.  The AAR contributes one libtor.so per Android ABI.
        maven { url "https://raw.githubusercontent.com/guardianproject/gpmaven/master" }
        maven {
                url "https://gitlab.com/api/v4/projects/32730908/packages/maven"
            }
    }
}
dependencies {
//    implementation "org.retroshare.service:libretroshare-MinApiLevel21-release:ebbc30e3"
    implementation "org.retroshare.service:libretroshare-MinApiLevel24-debug:ebbc30e3"
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
    // 0.4.8.16 is the newest release compatible with this project's JDK 21 /
    // AGP 8 toolchain. 0.4.9.11 is compiled as Java 24 bytecode.
    implementation "info.guardianproject:tor-android:0.4.8.16"
    implementation "info.guardianproject:jtorctl:0.4.5.7"

```}

@defnax

defnax commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author
image image

@defnax

defnax commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

As of August 17, 2026, this is an open, unmerged Tor integration split across two PRs:

What rs-mobile PR #42 implements

The mobile PR adds three operating modes:

  • Embedded Tor, running inside the Android app
  • External Tor, such as Orbot or another reachable Tor instance

Embedded Tor runtime

The app adds these dependencies:

implementation "info.guardianproject:tor-android:0.4.8.16"
implementation "info.guardianproject:jtorctl:0.4.5.7"

Version 0.4.8.16 is intentionally used because the newer 0.4.9.11 artifact was reportedly compiled as Java 24 bytecode, while the project currently builds with JDK 21/AGP 8.

A new native Android TorRuntimeManager:

  • Starts and stops Guardian Project’s TorService.
  • Writes a small torrc.
  • Runs Tor in client-only mode.
  • Exposes SOCKS on 127.0.0.1:39050.
  • Exposes the control protocol on 127.0.0.1:39051.
  • Disables control-password authentication with CookieAuthentication 0.
  • Stores the selected mode and endpoints in Android SharedPreferences.
  • Checks whether the SOCKS and control ports are reachable.
  • Starts Tor before the RetroShare Android service and stops it afterward.

Flutter communicates with this manager through a method channel:

Flutter UI
    ↓ Android method channel
TorRuntimeManager
    ↓
Guardian TorService / libtor.so
    ↓ SOCKS + control ports
libretroshare TorManager

External Tor support

In external mode, rs-mobile does not manage the other Tor process. It gives libretroshare:

  • Control address and port
  • SOCKS address and port
  • An empty control password

The default endpoint values are 127.0.0.1:9050 for SOCKS and 127.0.0.1:9051 for control.

Despite the UI saying “External Tor / Orbot,” the current UI does not appear to expose editable host and port fields. It therefore assumes those defaults when switching modes.

Hidden-node account creation

The previously inactive “Tor/I2P Hidden node” checkbox becomes a functional “Embedded Tor hidden node” option.

When selected, signup:

  1. Enables embedded Tor.
  2. Starts the Android Tor runtime.
  3. Configures libretroshare to attach to that runtime.
  4. Calls /rsLoginHelper/createLocationV2 with:
{
  "makeHidden": true,
  "makeAutoTor": true
}

makeHidden creates a hidden RetroShare location. makeAutoTor asks libretroshare’s existing AutoTor machinery to create and configure the onion service through the externally managed Tor control port.

Network settings UI

For hidden locations, the network screen now:

  • Displays the selected Tor mode.
  • Polls Tor listener status every two seconds.
  • Shows “Tor Active,” “Starting Tor…,” or “Tor Not Running.”
  • Displays SOCKS and control endpoints.
  • Retrieves the generated onion address and port.
  • Allows copying the onion address.
  • Prevents Tor from being disabled while a hidden location is active.
  • Hides the normal DHT status when Tor is active for a hidden location.

What is used from tor-android

The integration uses a relatively narrow part of Guardian Project’s project:

  • The packaged Android TorService.
  • Its bundled native libtor.so binaries for Android ABIs.
  • TorService.getTorrc() to locate the runtime configuration.
  • TorService.ACTION_START to start Tor.
  • Android service lifecycle calls to stop it.
  • The Guardian Maven repository for distributing the AAR.

The app does not modify Tor itself and does not embed the tor-android source tree. It consumes the published AAR.

jtorctl is declared as a dependency, but rs-mobile does not directly use its Java control API. Control-protocol interaction is handled by libretroshare’s existing C++ TorControl implementation.

What libretroshare PR #367 implements

The backend PR contains the changes that make Android-managed Tor usable by RetroShare.

Pre-login Tor configuration

It adds an unauthenticated JSON endpoint:

/rsTor/setExternalTorConnection

This is deliberately available before login because Tor must be configured before hidden-node initialization begins.

The endpoint passes the control and SOCKS endpoints to TorManager.

External-process mode

TorManager gains an explicit external-Tor mode. In that mode it:

  • Does not launch RetroShare’s normal TorProcess.
  • Connects directly to the supplied Tor control port.
  • Does not take ownership of the Tor process.
  • Does not terminate external Tor during shutdown.
  • Safely handles the absence of a TorProcess object.
  • Reconnects to the configured control endpoint if disconnected.
  • Uses the supplied SOCKS endpoint until Tor reports its listener.
  • Reports Tor as available even without a configured Tor executable.

This separation is important: Android owns the process lifecycle, while libretroshare owns hidden-service configuration and RetroShare routing.

Hidden account creation API

createLocationV2 is extended with:

bool makeHidden = false;
bool makeAutoTor = false;

The JSON API accepts those fields and forwards them to RsAccounts::createNewAccount. Existing callers remain compatible because both parameters default to false.

Hidden-service information

RsTor::getHiddenServiceInfo is exposed through the JSON API. rs-mobile uses:

/rsTor/getHiddenServiceInfo

to obtain:

  • Service ID
  • Full .onion hostname
  • Public onion-service port
  • Local target address
  • Local target port

This is what drives the onion-address display in network settings.

Android compatibility

The PR also disables a Linux “must run on the main thread” assertion on Android:

#elif defined(__linux__) && !defined(__ANDROID__)

Android defines __linux__, but its service/thread model does not satisfy that desktop-Linux assertion.

Important integration gap

Both PRs are still open. More importantly, the final rs-mobile branch currently declares:

implementation "org.retroshare.service:libretroshare-MinApiLevel24-debug:ebbc30e3"

ebbc30e3 is the base commit of libretroshare PR #367, not its current head (0a03aed3…). Therefore, unless that Maven artifact was rebuilt or replaced without changing its version, it does not contain the new endpoints.

Consequences with the older backend:

  • /rsTor/setExternalTorConnection will not exist.
  • /rsTor/getHiddenServiceInfo will not exist.
  • createLocationV2 will not understand makeHidden and makeAutoTor.
  • Embedded Tor itself may start, but complete hidden-node creation will not work.

The mobile code intentionally catches some missing-endpoint errors, so the UI can still load with older AARs. That compatibility behavior should not be mistaken for complete Tor functionality—the libretroshare PR must be included in the Android backend artifact for the full flow to work.

@defnax

defnax commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author
tor-rsmobile

@defnax

defnax commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

@G10h4ck @csoler

@defnax

defnax commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

@csoler @G10h4ck

@G10h4ck

G10h4ck commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Having setExternalTorConnection unauthenticated is quite dangerous. Just as an example any app installed on the device or even a web page loaded on your browser capable of reaching 127.0.0.1 via http, can use it to change your proxy configuration and redirecting you to a malevolous proxy. An alternative solution must be found.

@defnax

defnax commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Having setExternalTorConnection unauthenticated is quite dangerous. Just as an example any app installed on the device or even a web page loaded on your browser capable of reaching 127.0.0.1 via http, can use it to change your proxy configuration and redirecting you to a malevolous proxy. An alternative solution must be found.

but private coockie is used other apps then cant use it
RetroShare/rs-mobile@b2b7c2a

https://gist.github.com/ndsamuelson/3c83f38ae470c82ff87d2653af11716b
https://spec.torproject.org/control-spec/implementation-notes.html

image

TorControl::connect() sets the status to Connecting before attempting the
synchronous TCP connection, but never reset it when the connection failed,
so the status stayed at Connecting forever and TorManager::threadTick()
(which only retries from the NotConnected state) never attempted to
connect again. With an external Tor (e.g. Orbot not started yet, or a
wrong control port) this was the main failure mode: the whole auto-Tor
login sequence hung forever without reporting any error.

TorControl::reconnect() had the same problem, plus it ignored the result
of connectToHost() entirely: on success the status stayed at Connecting
(so authentication was never attempted after an Error recovery), and on
failure it was equally stuck.

Now both methods fall back to NotConnected on failure, so the manager
thread retries, and reconnect() properly enters SocketConnected on
success.
…) thread safe

threadTick() runs every 50ms and the connection attempt is synchronous,
so retrying from the NotConnected state (now reachable again after a
failed attempt) would hammer the control port with up to 20 connections
per second. Throttle attempts to one every 2 seconds, and restore the
connection log line (with an external-Tor variant) that told which
endpoint is being tried.

RsTor::instance() lazily creates the TorManager and used platform
asserts to require the main thread, which was the guard for that
unsynchronized initialisation. But RsTor methods are exposed through
the JSON API and thus legitimately reached from restbed worker threads:
any /rsTor/* call - including the new setExternalTorConnection endpoint
and the auto-Tor login path - aborted debug builds on Linux and Apple
platforms (the __ANDROID__ exception covered Android only, not iOS).
Protect the initialisation with a mutex instead, and drop the asserts.
startAutoTor() waited forever for Tor to become ready, with no timeout
and no error to break the loop when the control connection could not be
established at all (external Tor not running, wrong control port). Since
this runs synchronously inside RsLoginHelper::attemptLogin(), a mobile
or headless login on an auto-Tor node would simply never return.

Give up with an explicit error if the control link has not been seen up
once within 30 seconds. Once the control connection has been observed,
keep waiting indefinitely for Tor to bootstrap, as before.
Tor: fix stuck control connection, endless auto-Tor login, off-main-thread assert
@defnax

defnax commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@G10h4ck @csoler
but cookie is enabled by default on rs mobile when we use embedded tor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants