ProxyDroid Documentation

repository·master·Indexed 25 days ago

https://github.com/madeye/proxydroid

A modern Android global proxy application using a VPN-first architecture to tunnel device traffic through SOCKS5 or HTTP proxies without root access. It features a Rust-based tun2socks engine (proxydroid-tun2socks v0.1.0) using netstack-smoltcp, a Jetpack Compose UI, and support for per-app bypass via Android's VpnService.

Tokens
3.9K
Snippets
9
Records
24
Agent score
82%

What's inside ProxyDroid

  1. Overview of ProxyDroid Architecture

    master

    ProxyDroid is a global Android proxy application that uses a VPN-first architecture to forward device traffic to upstream SOCKS5 or HTTP proxies without requiring root access.

    Key architectural components:

    • VpnService: Uses the standard Android VpnService to capture IP packets on a TUN device, allowing for per-app bypass via the addDisallowedApplication API.
    • Rust tun2socks: A packet-to-socket bridge implemented in Rust using netstack-smoltcp. This core logic is invoked from Kotlin via JNI.
    • Jetpack Compose: The user interface is built with Compose and Material 3.
    • Upstream Support: Supports SOCKS5 (with optional username/password auth) and HTTP CONNECT (with optional Basic auth).
  2. Prerequisites for building ProxyDroid

    master

    To build ProxyDroid from source, ensure the following environment is configured:

    • JDK: Version 17 (Note: Gradle 8.x does not support JDK 21+).
    • Android SDK: compileSdk 36 must be installed.
    • Android NDK: Version 25.1.8937393.
    • CMake: Version 3.22.1.
    • Rust: Stable toolchain with the following Android targets installed:
      • aarch64-linux-android
      • armv7-linux-androideabi
      • i686-linux-android
      • x86_64-linux-android

    Important Note on AGP: Android Gradle Plugin (AGP) is pinned to 8.1.2 and Kotlin to 1.9.10. Do not upgrade AGP without verifying the rust-android-gradle 0.9.6 mergeJniLibFolders duplicate-resources interaction.

  3. Build ProxyDroid via Command Line

    master

    Use ./gradlew to build the APKs.

    To build a debug APK:

    ./gradlew assembleDebug

    Location: app/build/outputs/apk/debug/

    To build a release APK:

    ./gradlew assembleRelease

    Note: Release builds require a signing configuration to be signed. If signing keys are missing, the build will produce an unsigned APK.

    ./gradlew assembleDebug      # debug APK at app/build/outputs/apk/debug/
    ./gradlew assembleRelease    # release APK; requires signing config (see below)
  4. Build ProxyDroid using Android Studio

    master
    1. Open the project root in Android Studio.
    2. Allow Gradle to sync. The cargoBuild task will automatically run Cargo to build the JNI libraries and feed them into the merged APK.
    3. Select Build > Make Project.
  5. Run Integration Tests (Emulator ↔ Host SOCKS5)

    master

    The HostSocks5ProxyIntegrationTest validates that the app can route HTTP requests through a SOCKS5 proxy running on the host machine. The emulator accesses the host via the alias 10.0.2.2.

    Step 1: Start the SOCKS5 proxy on the host

    python3 scripts/socks5_test_server.py --host 0.0.0.0 --port 1080

    Step 2: Run the instrumentation test on an AVD

    ./gradlew connectedAndroidTest \
      -Pandroid.testInstrumentationRunnerArguments.class=org.proxydroid.HostSocks5ProxyIntegrationTest

    Customizing Test Parameters You can override the default test parameters using the following Gradle properties:

    • socksHost
    • socksPort
    • targetHost
    • targetPort

    Example usage:

    ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.socksHost=10.0.2.2 -Pandroid.testInstrumentationRunnerArguments.socksPort=1080
    # Terminal 1 — start the SOCKS5 proxy on the host:
    python3 scripts/socks5_test_server.py --host 0.0.0.0 --port 1080
    
    # Terminal 2 — boot any AVD, then run the instrumentation test:
    ./gradlew connectedAndroidTest \
      -Pandroid.testInstrumentationRunnerArguments.class=org.proxydroid.HostSocks5ProxyIntegrationTest
  6. Supported Proxy Kinds in ProxyDroid

    master

    ProxyDroid supports the following upstream proxy protocols via the ProxyKind enum:

    VariantDescription
    Socks5SOCKS5 protocol. Supports optional username/password authentication via RFC 1929.
    Socks4SOCKS4 protocol.
    HttpHTTP CONNECT protocol. Supports optional Basic authentication.
    HttpsHTTPS protocol. Uses HTTP CONNECT over a TLS-wrapped connection to the proxy.

    Note: ProxyKind::parse(s: &str) can be used to convert string identifiers (case-insensitive) into these variants. If an unknown string is provided, it defaults to Socks5.

  7. Configure Signing for Release Builds

    master

    To sign the release variant, create a local.properties file in the project root and provide the following keys:

    KEYSTORE_PATH=/absolute/path/to/keystore.jks
    KEYSTORE_PASSWORD=...
    KEY_ALIAS=...
    KEY_PASSWORD=...
  8. Configure upstream proxy settings with UpstreamConfig

    master

    The UpstreamConfig struct defines how the tun2socks engine connects to your proxy server. It supports multiple proxy protocols and optional authentication.

    Supported Proxy Types (ProxyKind)

    • Socks5: SOCKS5 protocol (supports optional username/password).
    • Socks4: SOCKS4 protocol.
    • Http: HTTP CONNECT protocol (supports optional Basic auth).
    • Https: HTTPS protocol (HTTP CONNECT over TLS to the proxy).

    Configuration Fields

    • kind: The ProxyKind enum value.
    • host: The hostname or IP address of the proxy.
    • port: The port number of the proxy.
    • user: (Optional) Username for proxy authentication.
    • password: (Optional) Password for proxy authentication.
    #[derive(Clone)]
    pub struct UpstreamConfig {
        pub kind: ProxyKind,
        pub host: String,
        pub port: u16,
        pub user: Option<String>
        pub password: Option<String>,
    }
  9. Start the tun2socks engine using start()

    master

    The start function initializes the tun2socks engine. It takes a TUN file descriptor (provided by Android's VpnService) and an UpstreamConfig object.

    Behavior

    • Concurrency: It spawns the engine in a background Tokio task. It is not a blocking call.
    • Error Handling: If the engine is already running, it returns Tun2SocksError::AlreadyRunning. It also performs a synchronous check on the DoH (DNS over HTTPS) client configuration; if the proxy URL is invalid, it returns an error immediately.
    • Non-blocking I/O: The function automatically sets the provided TUN file descriptor to O_NONBLOCK mode.

    Parameters

    • fd: The raw integer file descriptor for the Android TUN interface.
    • cfg: An UpstreamConfig instance defining the proxy connection.

    Returns

    • Ok(()) if the engine started successfully.
    • Err(Tun2SocksError) if the engine was already running or if DoH initialization failed.
    pub fn start(fd: i32, cfg: UpstreamConfig) -> Result<(), Tun2SocksError> {
        // ...
    }
  10. Encode and decode address lists

    master

    ProxyDroid provides utility methods to encode and decode arrays of strings (typically used for proxyApps or bypassAddrs) using Base64 encoding and a pipe (|) delimiter. This ensures that complex strings or special characters in app names or addresses are safely stored.

    • encodeAddrs(addrs: Array<String>?): String: Converts an array of strings into a single pipe-delimited Base64 string.
    • decodeAddrs(encoded: String?): Array<String>: Reverses the encoding to return the original array.
  11. JNI Entry Points for Tun2Socks (Android Integration)

    master

    The proxydroid-tun2socks Rust library provides a stable JNI ABI for the Android application via the org.proxydroid.utils.Tun2SocksHelper class. These functions allow the Android JVM to control the lifecycle of the tun2socks engine.

    Important ABI Note

    The JNI symbol names (e.g., Java_org_proxydroid_...) form a stable ABI. Renaming these functions will break the connection with the Android app.

    Return Codes for nativeStart

    When calling nativeStart from the JVM, the function returns a jint indicating the result:

    • 0: Success.
    • -1: An expected error occurred (details are logged via the Android logger).
    • -2: The Rust implementation panicked (to prevent undefined behavior across the FFI boundary).

    Lifecycle Management

    • Start: Use nativeStart to initialize the tun2socks loop with the provided TUN file descriptor and proxy configuration.
    • Stop: Use nativeStop to terminate the tun2socks loop and release the stored VpnService reference.
    /* 
    JNI Symbols used by the Android app:
    
    Java_org_proxydroid_utils_Tun2SocksHelper_nativeStart
    Java_org_proxydroid_utils_Tun2SocksHelper_nativeStop
    */
  12. Start the tun2socks engine with Tun2SocksHelper.start()

    master

    Use Tun2SocksHelper.start() to initialize the Rust-based tun2socks engine. This method is non-blocking; the Rust side spawns its own tokio runtime and returns once the runtime has been handed the TUN file descriptor (tunFd).

    Returns true if the engine started successfully, or false if an instance is already running or if the native side reported a failure (non-zero return code).