Add VPN mode support without requiring root permissions#32
Open
madeye wants to merge 15 commits into
Open
Conversation
This commit adds a new VPN-based proxy mode that allows ProxyDroid to work without root permissions by using Android's VpnService API and tun2socks. Key changes: - Add ProxyDroidVpnService class implementing VpnService - Add tun2socks native JNI library for TUN-to-SOCKS conversion - Add Tun2SocksHelper for managing the tun2socks process - Add LocalProxyServer for HTTP-to-SOCKS proxy conversion - Update AndroidManifest.xml with VPN permission and service declaration - Modify ProxyDroid.java to support both VPN and root modes - Add isVpnMode preference toggle (enabled by default) - Update minSdkVersion to 21 for per-app VPN filtering support - Bump version to 3.3.0 The VPN mode routes all traffic through a TUN interface which is then forwarded through the configured proxy server via tun2socks. For HTTP proxies, a local SOCKS server converts the traffic appropriately.
This commit adds a full tun2socks implementation with: - Core TCP/IP packet processing (tun2socks.c/h) - SOCKS5 client with authentication support - TCP connection tracking and state machine - UDP packet forwarding (DNS support) - Proper IP/TCP/UDP checksum calculation - Connection timeout handling The implementation reads packets from the TUN device, parses IP/TCP/UDP headers, establishes SOCKS5 connections to the proxy server, and relays data bidirectionally between the TUN interface and SOCKS proxy. Key features: - Full TCP state machine (SYN, ACK, FIN, RST handling) - SOCKS5 CONNECT command for TCP connections - DNS query forwarding via UDP - Per-connection send/receive buffers - Non-blocking I/O with poll() - Connection idle timeout cleanup
This adds a CI workflow that: - Builds the debug APK on every push and pull request - Sets up JDK 11 and Android SDK with NDK - Caches Gradle dependencies for faster builds - Creates dummy google-services.json for CI builds - Uploads the built APK as an artifact - Runs Android Lint checks in a separate job The workflow triggers on: - Pushes to master, main, and claude/* branches - Pull requests to master and main branches
- Replace deprecated jcenter() with mavenCentral() - Remove deprecated Fabric.io maven repository - Update from deprecated io.fabric plugin to com.google.firebase.crashlytics - Update google-services plugin to 4.3.15 - Replace deprecated com.crashlytics.sdk.android:crashlytics with com.google.firebase:firebase-crashlytics:18.4.3 - Fix IPPROTO_* redefinition warnings in tun2socks.h by using #ifndef guards
- Upgrade Gradle from 5.4.1 to 7.6.3 - Upgrade Android Gradle Plugin from 3.5.3 to 7.4.2 - Update app/build.gradle for AGP 7.x syntax: - compileSdkVersion → compileSdk - minSdkVersion → minSdk - targetSdkVersion → targetSdk - Remove buildToolsVersion (optional in AGP 7+) - Add namespace for AGP 7+ requirement - Update configurations to use implementation instead of compile
- Remove specific CMake version requirement from app/build.gradle (let AGP use default CMake version) - Install CMake 3.22.1 in GitHub Actions workflow alongside NDK
- Remove package attribute (using namespace in build.gradle) - Add android:exported to activities with intent-filters (required for API 31+) - Add android:exported to receivers with intent-filters - Add POST_NOTIFICATIONS permission for Android 13 - Add FOREGROUND_SERVICE_SPECIAL_USE permission - Add foregroundServiceType="specialUse" to services - Add PROPERTY_SPECIAL_USE_FGS_SUBTYPE property to services - Limit WRITE_EXTERNAL_STORAGE to maxSdkVersion 28
- Remove foregroundServiceType="specialUse" (only available in API 34+) - Remove FOREGROUND_SERVICE_SPECIAL_USE permission - Remove PROPERTY_SPECIAL_USE_FGS_SUBTYPE properties - Keep services without foregroundServiceType for API 33 compatibility
Convert switch statements using R.id.* values to if-else statements because resource IDs are no longer compile-time constants in AGP 7.x.
- Update Gradle wrapper from 7.6.3 to 8.13 - Update Android Gradle Plugin from 7.4.2 to 8.13.2 - Update Google Services plugin from 4.3.15 to 4.4.1 - Convert app/build.gradle to plugins block syntax (required by AGP 8+) - Replace switch on R.id with if-else chain in BypassListActivity (R.id fields are no longer compile-time constants in AGP 8+) - Fix arc4random_addrandom compilation error in evutil_rand.c Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add JUnit and Mockito test dependencies to build.gradle - Create LocalHttpProxyTest with comprehensive tests: - SOCKS5 handshake protocol - IPv4 and domain name connection handling - HTTP proxy authentication - Proxy connection failure handling - Data relay functionality - Server start/stop lifecycle - Concurrent connection handling - Add test job to GitHub Actions workflow with: - Unit test execution with stacktrace and info output - Test result artifact upload - Test summary output for debugging
- Add NDK and CMake installation to test job (required for native code compilation) - Add ndk.dir to local.properties for test job - Update compileOptions to Java 11 (required for Mockito 4.x) - Use explicit testDebugUnitTest task instead of generic test - Improve test summary output with better XML result parsing
AGP 8.x requires NDK 25.x or later. Updated from NDK 21.4.7075529 to 25.1.8937393 (the default for AGP 8.x) in both the GitHub Actions workflow and app/build.gradle.
madeye
pushed a commit
that referenced
this pull request
Feb 1, 2026
This implements PR #32 features: - Add ProxyDroidVpnService extending Android's VpnService API - Add native tun2socks library for TUN to SOCKS5 conversion - Add LocalProxyServer for HTTP-to-SOCKS protocol conversion - Add Tun2SocksHelper JNI wrapper for native library - Update build.gradle to SDK 33, Gradle 8.x, NDK 25.1 - Add VPN mode toggle in preferences (enabled by default) - Update AndroidManifest with VPN service declaration and permissions https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL
madeye
added a commit
that referenced
this pull request
Feb 1, 2026
* Add VPN mode support without requiring root permissions This implements PR #32 features: - Add ProxyDroidVpnService extending Android's VpnService API - Add native tun2socks library for TUN to SOCKS5 conversion - Add LocalProxyServer for HTTP-to-SOCKS protocol conversion - Add Tun2SocksHelper JNI wrapper for native library - Update build.gradle to SDK 33, Gradle 8.x, NDK 25.1 - Add VPN mode toggle in preferences (enabled by default) - Update AndroidManifest with VPN service declaration and permissions https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Add GitHub Actions CI workflow and unit tests - Add android-build.yml workflow for CI/CD with: - JDK 17 and Android SDK setup - Gradle build, unit tests, and lint - APK and test results artifact upload - Add LocalHttpProxyTest with comprehensive unit tests for: - SOCKS5 handshake (auth and no-auth) - SOCKS5 connect requests (IPv4, domain, IPv6) - HTTP CONNECT proxy requests - Protocol parsing and encoding https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Add local HTTP proxy tests with Android emulator integration - Add emulator-test job to GitHub Actions workflow that: - Starts an HTTP CONNECT proxy server on the host - Installs the app on Android emulator - Configures proxy settings via adb - Verifies VPN service declaration and native libraries - Tests app startup and configuration handling - Add test_http_proxy.py: Python HTTP CONNECT proxy for testing - Supports authentication - Logs all CONNECT requests - Relays data between client and target - Add run_integration_tests.sh: Integration test script - Automates emulator testing workflow - Configures preferences via adb - Runs connectivity verification tests - Update app/build.gradle with additional androidTest dependencies https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Remove unused imports and constants from unit tests Clean up test file to remove unused imports (ServerSocket, Socket, InputStream, OutputStream, etc.) and unused port constants that were left over from the previous socket-based test implementation. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix CI lint job by adding NDK/CMake installation The lint job was failing because it couldn't compile native code without NDK and CMake installed. Also removed unnecessary dummy google-services.json from lint job since Firebase is not used. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix build error: remove API 34+ foregroundServiceType The 'specialUse' foregroundServiceType and FOREGROUND_SERVICE_SPECIAL_USE permission were added in Android 14 (API 34), but the app targets API 33. VPN services don't need a specific foregroundServiceType since VpnService has special handling built-in. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Make gradlew executable https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix JUnit exclusion to not affect test configurations The previous configuration excluded JUnit from all configurations which broke unit tests. Now JUnit is only excluded from implementation scope to prevent json-simple's transitive JUnit dependency from being included in the APK, while still allowing testImplementation to use JUnit. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Remove Firebase/AdMob dependencies and fix R.id switch statements - Remove Firebase Analytics from ProxyDroidApplication - Remove AdMob (AdView, AdSize, AdRequest) from ProxyDroid - Convert switch statement with R.id.* to if-else in BypassListActivity (R.id fields are not constant expressions with Android Gradle Plugin) - Remove unused getLayout method, ViewParent and LinearLayout imports https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Remove remaining Firebase Analytics references Remove firebaseAnalytics calls from: - AppManager.java - FileChooser.java - ProxyDroidService.java (onDestroy and onStart) https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix configurations syntax in build.gradle Use configurations.implementation instead of configurations { implementation { } } to properly exclude JUnit from implementation configuration. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix JUnit exclusion: exclude only from json-simple dependency The previous configuration excluded JUnit from all implementation configurations which was also blocking it from test configurations. Now JUnit is only excluded from the json-simple dependency which incorrectly pulls it in as a compile dependency. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix shell quoting in emulator test script Use heredoc (cat << 'PREFS_EOF') instead of variable assignment to avoid shell parsing issues with XML special characters. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix emulator test: create prefs.xml in separate step The android-emulator-runner action executes script lines individually, so heredocs don't work within the script block. Move the prefs.xml creation to a separate step that runs before the emulator. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Add Python cache to .gitignore https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Move emulator test script to separate file Extract inline emulator test script from workflow YAML into scripts/run_emulator_tests.sh for easier maintenance and testing. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Enable emulator snapshots in CI Remove -no-snapshot-save flag to allow emulator snapshots, which can speed up boot times on subsequent runs. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix emulator test script execution Use 'bash scripts/run_emulator_tests.sh' instead of './scripts/...' to ensure the script is found and executed correctly. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL * Fix line endings in emulator test script Convert CRLF to LF line endings to fix bash syntax error. https://claude.ai/code/session_01XDv7Fhpou2mSbpsV56cWxL --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a VPN-based proxy mode to ProxyDroid that doesn't require root permissions. Users can now choose between the traditional root-based service and a new VPN-based service that uses tun2socks to redirect traffic through a SOCKS proxy.
Key Changes
ProxyDroidVpnServicethat extends Android'sVpnServiceto provide system-wide proxy functionality without roottun2socks_jni.cpp) that handles TUN interface management and SOCKS proxy redirection for TCP/UDP trafficisVpnModepreference to allow users to switch between VPN and root-based modesminSdkVersionfrom 16 to 21 to support VPN Service APIsProxyedAppclass with package name tracking for per-app VPN filteringonActivityResultcallbackImplementation Details
Testing Recommendations