From eebf63af29b9486f47debb515e03a035756c66e4 Mon Sep 17 00:00:00 2001 From: Maxb Date: Fri, 28 Mar 2025 12:46:11 -0700 Subject: [PATCH 1/2] Improve README --- README.md | 112 +++++++++++++++++++++++++++++++++---------------- screenshot.png | Bin 0 -> 55793 bytes 2 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 screenshot.png diff --git a/README.md b/README.md index 65adad6..1c3e52e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,66 @@ -# Pseudotcp Example Apps +# ๐Ÿ”’ Pseudotcp Example App -This is an example android app which uses [Android's VPN APIs](https://developer.android.com/develop/connectivity/vpn) and gomobile bindings in order to demonstrate the integration with and utility of Invisv's pseudotcp library. +[![Build Status](https://img.shields.io/github/actions/workflow/status/invisv-privacy/pseudotcp-example-app/end2endtest.yml?branch=main&style=flat-square)](https://github.com/invisv-privacy/pseudotcp-example-app/actions) +[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +An Android example application demonstrating the integration of [Invisv's pseudotcp library](https://github.com/invisv/pseudotcp) using [Android's VPN APIs](https://developer.android.com/develop/connectivity/vpn) and gomobile bindings. -## Build -The go code is built with [gomobile](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile). You can use the [build.sh](./app/src/go/build.sh) script for convenience: -``` +## ๐Ÿ” Overview + +This application demonstrates how to implement a VPN service in Android that routes traffic through a MASQUE proxy using the pseudotcp library. It showcases: + +- Integration with Android's VPN Service API +- Go-to-Java bindings using gomobile +- Implementation of pseudotcp for reliable transport +- MASQUE client for proxy communication + +Pseudotcp example app screenshot + +## ๐Ÿ› ๏ธ Prerequisites + +- Android Studio (latest version recommended) +- Go 1.23 or later +- [gomobile](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile) +- Docker (for testing) +- Android SDK with API level 33 or higher + +## ๐Ÿ—๏ธ Building + +### Building the Go Code + +The Go code is built with [gomobile](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile). Use the provided build script: + +```bash $ cd app/src/go -app/src/go $ build.sh -Running gomobile bind... -Moved .aar to Android libs. REMEMBER to sync project with gradle files in Android! +$ ./build.sh +``` + +This will generate an `.aar` file and move it to the appropriate Android libs directory. + +> **Important**: After building the Go code, remember to sync your project with Gradle files in Android Studio to reload the newly built library. + +### Building the Android App + +Open the project in Android Studio and build the app using the standard build process: + +```bash +$ ./gradlew assembleDebug ``` -At that point, like it says, use the "sync project with gradle files" command in android studio in order for it to reload the newly built .aar library. -## Test -This repo includes a full end2end test for exercising the entire network stack, from android VPN service code -> gomobile bindings -> pseudotcp -> masque client. +## ๐Ÿ“ฑ Usage + +1. Install the app on your Android device or emulator +2. Launch the app +3. Enter the proxy IP and port (defaults to 127.0.0.1:8444) +4. Toggle the switch to enable/disable the VPN service +5. The app will route all traffic through the specified MASQUE proxy + +## ๐Ÿงช Testing + +This repo includes a comprehensive end-to-end test that exercises the entire network stack. + +### Testing Architecture -The testing architecture looks like this: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Docker containers โ”‚ @@ -48,41 +92,37 @@ The emulator uses qemu and a base android image to create a virtualized android We use docker to run an [h2o](https://github.com/h2o/h2o) MASQUE proxy and another very simple echo server. The echo server responds to HTTP requests with information about the HTTP request, including the request IP. -Inside the automated test we can then start, check our initial reported IP, enable our sample app service, check our reported IP, and assert that the new reported IP is that of the proxy, prving that packets from the android host device are now passing through the MASQUE proxy as expected. +Inside the automated test we can then start, check our initial reported IP, enable our sample app service, check our reported IP, and assert that the new reported IP is that of the proxy, proving that packets from the android host device are now passing through the MASQUE proxy as expected. -### Running -In order to run the test you must first start the dockerized h2o server and the echo server: +### Setting Up the Test Environment -```sh +Start the dockerized h2o server and echo server: + +```bash $ docker-compose up -d ``` -The [docker-compose file](./docker-compose.yml) includes a custom network with a subnet of `172.25.0.0/24` assuming that range is unlikely to be used elsewhere. If that clashes with your network environment, you'll need to update the addresses in the docker-compose file as well as in the [end2end test](./app/src/androidTest/java/com/invisv/pseudotcpexampleapp/End2EndTest.java). +The [docker-compose file](./docker-compose.yml) creates a custom network with subnet `172.25.0.0/24`. If this conflicts with your network, update the addresses in both the docker-compose file and the [end2end test](./app/src/androidTest/java/com/invisv/pseudotcpexampleapp/End2EndTest.java). -After starting the docker services, you can then run the test. You'll need an actual device to run it on, whether that's qemu emulated or an actual physical device. [Android studio makes creating qemu emulated devices quite simple](https://developer.android.com/studio/run/managing-avds). +### Running Tests -Once you have an android device running, you can run the test through the Android Studio IDE or from the command line: +You can run tests through Android Studio or from the command line: -```sh +```bash $ ./gradlew connectedAndroidTest -Starting a Gradle Daemon (subsequent builds will be faster) - -> Configure project :app - -> Task :app:connectedDebugAndroidTest -Starting 1 tests on Pixel_5_API_33(AVD) - 13 - -Pixel_5_API_33(AVD) - 13 Tests 0/1 completed. (0 skipped) (0 failed) -Finished 1 tests on Pixel_5_API_33(AVD) - 13 - -BUILD SUCCESSFUL in 37s -61 actionable tasks: 11 executed, 50 up-to-date ``` -An html report will be then placed in `app/build/reports/androidTests/connected/debug/com.invisv.pseudotcpexampleapp.End2EndTest.html` +An HTML report will be generated at: +`app/build/reports/androidTests/connected/debug/com.invisv.pseudotcpexampleapp.End2EndTest.html` + +### Viewing Test Logs -`stdout` will not be outputted on the command line. In order to get logging and `stdout` from the automated test, you can use the [same command we use for CI](./.github/workflows/end2endtest.yml#92): +To capture logs during test execution: -```sh -$ adb logcat "System.out:D End2EndTest:D *:S" & LOGCAT_PID=$! ; ./gradlew connectedAndroidTest ; test_ret=$? ; if [ -n "$LOGCAT_PID" ] ; then kill $LOGCAT_PID; fi; exit $test_ret +```bash +$ adb logcat "System.out:D End2EndTest:D *:S" & LOGCAT_PID=$! ; \ + ./gradlew connectedAndroidTest ; \ + test_ret=$? ; \ + if [ -n "$LOGCAT_PID" ] ; then kill $LOGCAT_PID; fi; \ + exit $test_ret ``` diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..245329cade1ac63a5dfe9edb3268a9bc5184d0fd GIT binary patch literal 55793 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 A@c;k- literal 0 HcmV?d00001 From 8986b3ee3b9fdc701a3e2d07e5d353b167cc7eb7 Mon Sep 17 00:00:00 2001 From: Maxb Date: Fri, 28 Mar 2025 12:49:20 -0700 Subject: [PATCH 2/2] Zhuzh up build script Adds some fancy colors and things because why not :) --- app/src/go/build.sh | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/app/src/go/build.sh b/app/src/go/build.sh index 7d7e306..c0d53d2 100755 --- a/app/src/go/build.sh +++ b/app/src/go/build.sh @@ -1,12 +1,43 @@ -#!/bin/sh +#!/bin/bash +# Exit immediately if a command exits with a non-zero status set -e +# Define colors for better output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration ANDROID_API_VERSION=33 +OUTPUT_DIR="../../libs" +OUTPUT_FILE="bindings.aar" + +# Check if gomobile is installed +if ! command -v gomobile &> /dev/null; then + echo -e "${YELLOW}Error: gomobile is not installed.${NC}" + echo -e "Please install it with: ${BLUE}go install golang.org/x/mobile/cmd/gomobile@latest${NC}" + echo -e "Then initialize it with: ${BLUE}gomobile init${NC}" + exit 1 +fi + +go get golang.org/x/mobile/bind -echo 'Running gomobile bind...' +echo -e "${BLUE}=== Building Go code with gomobile ===${NC}" +echo -e "${GREEN}Target:${NC} Android API $ANDROID_API_VERSION" +echo -e "${GREEN}Output:${NC} $OUTPUT_FILE" +# Run gomobile bind +echo -e "${BLUE}Running gomobile bind...${NC}" gomobile bind -target android -androidapi $ANDROID_API_VERSION -ldflags=-extldflags=-Wl,-soname,libgojni.so -mkdir -p ../../libs && cp bindings.aar ../../libs/ -echo 'Moved .aar to Android libs. REMEMBER to sync project with gradle files in Android!' +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +# Copy the output file +cp "$OUTPUT_FILE" "$OUTPUT_DIR/" + +echo -e "${GREEN}โœ“ Successfully built and moved $OUTPUT_FILE to Android libs.${NC}" +echo -e "${YELLOW}IMPORTANT:${NC} Remember to sync project with Gradle files in Android Studio!" +echo -e "You can do this by clicking ${BLUE}'Sync Project with Gradle Files'${NC} in Android Studio."