Compare commits

...

10 Commits

Author SHA1 Message Date
afe7019f4f add new keystore for GitHub Workflow 2025-12-08 15:31:05 +08:00
a7738d9a5d Update CHROMIUM_VERSION
to 143.0.7499.41
2025-12-07 20:04:12 +08:00
23bb6c784d Allow setting environment variables from config 2025-09-26 22:43:28 +08:00
a30cf1b39b Fix test 2025-09-25 19:02:42 +08:00
20ce2c897c Support specifying multiple proxies 2025-09-24 20:45:00 +08:00
eb81e5d573 tools: Remove Android arm64 custom profile path 2025-09-14 23:22:32 +08:00
c952b7015d Support debug build 2025-09-14 23:02:47 +08:00
48c2bfd551 Add continuous integration and tests 2025-09-14 23:02:47 +08:00
aaa3e5104d Add apk build 2025-09-14 23:02:47 +08:00
216c9ce306 Add build scripts 2025-09-14 23:02:47 +08:00
46 changed files with 2371 additions and 29 deletions

581
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,581 @@
name: Build
on:
push:
branches: [master]
paths-ignore: [README.md]
release:
types: [published]
defaults:
run:
shell: bash
working-directory: src
env:
CACHE_EPOCH: 1
CCACHE_MAXSIZE: 200M
CCACHE_MAXFILES: 0
SCCACHE_CACHE_SIZE: 200M
jobs:
cache-toolchains-posix:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
src/qemu-user*.deb
key: toolchains-posix-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (Linux, OpenWrt)
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/
key: pgo-linux-openwrt-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache AFDO (Android)
uses: actions/cache@v4
with:
path: src/chrome/android/profiles/
key: afdo-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache Android NDK (Android)
uses: actions/cache@v4
with:
path: src/third_party/android_toolchain/ndk/
key: android-ndk-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- run: ./get-clang.sh
- run: EXTRA_FLAGS='target_os="android"' ./get-clang.sh
- run: |
if [ ! -f qemu-user*.deb ]; then
wget https://snapshot.debian.org/archive/debian/20250405T083429Z/pool/main/q/qemu/qemu-user_9.2.2%2Bds-1%2Bb2_amd64.deb
fi
cache-toolchains-win:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Cache toolchains
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
~/.cargo/bin/
~/bin/ninja.exe
key: toolchains-win-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (win64)
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/chrome-win64-*
key: pgo-win64-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (win32)
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/chrome-win32-*
key: pgo-win32-arm64-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- run: EXTRA_FLAGS='target_cpu="x64"' ./get-clang.sh
- run: EXTRA_FLAGS='target_cpu="x86"' ./get-clang.sh
- run: |
if [ ! -f ~/bin/ninja.exe ]; then
curl -LO https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-win.zip
unzip ninja-win.zip -d ~/bin
fi
cache-toolchains-mac:
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/chrome/build/pgo_profiles/chrome-mac-*
src/gn/
key: toolchains-pgo-mac-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- run: EXTRA_FLAGS='target_cpu="x64"' ./get-clang.sh
- run: EXTRA_FLAGS='target_cpu="arm64"' ./get-clang.sh
linux:
needs: cache-toolchains-posix
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
arch: [x64, x86, arm64, arm, mipsel, mips64el, riscv64, loong64]
env:
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}"'
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
src/qemu-user*.deb
key: toolchains-posix-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (Linux, OpenWrt)
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/
key: pgo-linux-openwrt-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Regenerate Debian keyring
run: |
rm -f ./build/linux/sysroot_scripts/keyring.gpg
GPG_TTY=/dev/null ./build/linux/sysroot_scripts/generate_keyring.sh
- name: Cache sysroot
uses: actions/cache@v4
with:
path: |
src/out/sysroot-build/bullseye/bullseye_*
src/out/sysroot-build/trixie/trixie_*
src/out/sysroot-build/sid/sid_*
key: sysroot-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |
sudo apt update
sudo apt install ninja-build pkg-config ccache bubblewrap
sudo apt remove -y qemu-user-binfmt
sudo dpkg -i qemu-user*.deb
# libc6-i386 interferes with x86 build
sudo apt remove libc6-i386
- run: ./get-clang.sh
- run: ccache -z
- run: ./build.sh
- run: ccache -s && ccache --evict-older-than 1d
- run: ../tests/basic.sh out/Release/naive
- name: Pack naiveproxy assets
run: |
mkdir ${{ env.BUNDLE }}
cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
tar cJf ${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
openssl sha256 out/Release/naive >sha256sum.txt
echo "SHA256SUM=$(cut -d' ' -f2 sha256sum.txt)" >>$GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: ${{ env.BUNDLE }}.tar.xz naive executable sha256 ${{ env.SHA256SUM }}
path: src/sha256sum.txt
- name: Upload naiveproxy assets
if: ${{ github.event_name == 'release' }}
run: gh release upload "${GITHUB_REF##*/}" ${{ env.BUNDLE }}.tar.xz --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
android:
needs: cache-toolchains-posix
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- arch: x64
abi: x86_64
- arch: x86
abi: x86
- arch: arm64
abi: arm64-v8a
- arch: arm
abi: armeabi-v7a
env:
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}" target_os="android"'
BUNDLE: naiveproxy-plugin-${{ github.event.release.tag_name || 'v1.1.1.1-1' }}-${{ matrix.abi }}.apk
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
src/qemu-user*.deb
key: toolchains-posix-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache AFDO (Android)
uses: actions/cache@v4
with:
path: src/chrome/android/profiles/
key: afdo-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache Android NDK (Android)
uses: actions/cache@v4
with:
path: src/third_party/android_toolchain/ndk/
key: android-ndk-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache sysroot
uses: actions/cache@v4
with:
path: src/out/sysroot-build/android/
key: sysroot-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |
sudo apt update
sudo apt install ninja-build pkg-config ccache bubblewrap
sudo apt remove -y qemu-user-binfmt
sudo dpkg -i qemu-user*.deb
# libc6-i386 interferes with x86 build
sudo apt remove libc6-i386
- run: ./get-clang.sh
- run: ccache -z
- run: ./build.sh
- run: ccache -s && ccache --evict-older-than 1d
- run: ./get-android-sys.sh
- run: ../tests/basic.sh out/Release/naive
- name: Gradle cache
uses: actions/cache@v4
with:
path: ~/.gradle
key: gradle-${{ hashFiles('**/*.gradle.kts') }}
- name: Create APK
working-directory: apk
env:
APK_ABI: ${{ matrix.abi }}
APK_VERSION_NAME: ${{ github.event.release.tag_name || 'v1.1.1.1-1' }}
KEYSTORE_PASS: ${{ secrets.KEYSTORE_PASS }}
run: |
mkdir -p app/libs/$APK_ABI
cp ../src/out/Release/naive app/libs/$APK_ABI/libnaive.so
./gradlew :app:assembleRelease
openssl sha256 app/build/outputs/apk/release/${{ env.BUNDLE }} >sha256sum.txt
echo "SHA256SUM=$(cut -d' ' -f2 sha256sum.txt)" >>$GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: ${{ env.BUNDLE }} sha256 ${{ env.SHA256SUM }}
path: apk/sha256sum.txt
- name: Upload naiveproxy assets
if: ${{ github.event_name == 'release' }}
working-directory: apk/app/build/outputs/apk/release
run: gh release upload "${GITHUB_REF##*/}" ${{ env.BUNDLE }} --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
win:
needs: cache-toolchains-win
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
arch: [x64, x86, arm64]
env:
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}"'
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
- name: Cache toolchains
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
~/.cargo/bin/
~/bin/ninja.exe
key: toolchains-win-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (win64)
if: ${{ matrix.arch == 'x64' }}
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/chrome-win64-*
key: pgo-win64-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (win32)
if: ${{ matrix.arch != 'x64' }}
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/chrome-win32-*
key: pgo-win32-arm64-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v4
with:
path: ~/AppData/Local/Mozilla/sccache
key: ccache-win-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-win-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: ./get-clang.sh
- run: ~/.cargo/bin/sccache -z
- run: ./build.sh
- run: ~/.cargo/bin/sccache -s
- run: ../tests/basic.sh out/Release/naive
# No real or emulated environment is available to test this.
if: ${{ matrix.arch != 'arm64' }}
- name: Pack naiveproxy assets
run: |
mkdir ${{ env.BUNDLE }}
cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
7z a ${{ env.BUNDLE }}.zip ${{ env.BUNDLE }}
openssl sha256 out/Release/naive.exe >sha256sum.txt
echo "SHA256SUM=$(cut -d' ' -f2 sha256sum.txt)" >>$GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: ${{ env.BUNDLE }}.zip naive executable sha256 ${{ env.SHA256SUM }}
path: src/sha256sum.txt
- name: Upload naiveproxy assets
if: ${{ github.event_name == 'release' }}
run: gh release upload "${GITHUB_REF##*/}" ${{ env.BUNDLE }}.zip --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
mac:
needs: cache-toolchains-mac
runs-on: macos-15
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
env:
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}"'
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
- name: Cache toolchains and PGO
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/chrome/build/pgo_profiles/chrome-mac-*
src/gn/
key: toolchains-pgo-mac-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v4
with:
path: ~/Library/Caches/ccache
key: ccache-mac-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-mac-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: brew install ninja ccache
- run: pip install setuptools
- run: ./get-clang.sh
- run: ccache -z
- run: ./build.sh
- run: ccache -s && ccache --evict-older-than 1d
- run: ../tests/basic.sh out/Release/naive
# No real or emulated environment is available to test this.
if: ${{ matrix.arch != 'arm64' }}
- name: Pack naiveproxy assets
run: |
mkdir ${{ env.BUNDLE }}
cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
tar cJf ${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
openssl sha256 out/Release/naive >sha256sum.txt
echo "SHA256SUM=$(cut -d' ' -f2 sha256sum.txt)" >>$GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: ${{ env.BUNDLE }}.tar.xz naive executable sha256 ${{ env.SHA256SUM }}
path: src/sha256sum.txt
- name: Upload naiveproxy assets
if: ${{ github.event_name == 'release' }}
run: gh release upload "${GITHUB_REF##*/}" ${{ env.BUNDLE }}.tar.xz --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
openwrt:
needs: cache-toolchains-posix
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
openwrt: "target=x86 subtarget=64"
target_cpu: x64
- arch: x86
openwrt: "target=x86 subtarget=geode"
target_cpu: x86
- arch: aarch64_cortex-a53
openwrt: "target=sunxi subtarget=cortexa53"
target_cpu: arm64
extra: 'arm_cpu="cortex-a53"'
- arch: aarch64_cortex-a53-static
openwrt: "target=sunxi subtarget=cortexa53"
target_cpu: arm64
extra: 'arm_cpu="cortex-a53" build_static=true use_allocator_shim=false use_partition_alloc=false'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: aarch64_cortex-a72
openwrt: "target=mvebu subtarget=cortexa72"
target_cpu: arm64
extra: 'arm_cpu="cortex-a72"'
- arch: aarch64_cortex-a72-static
openwrt: "target=mvebu subtarget=cortexa72"
target_cpu: arm64
extra: 'arm_cpu="cortex-a72" build_static=true use_allocator_shim=false use_partition_alloc=false'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: aarch64_cortex-a76
openwrt: "target=bcm27xx subtarget=bcm2712"
target_cpu: arm64
extra: 'arm_cpu="cortex-a76"'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: aarch64_generic
openwrt: "target=layerscape subtarget=armv8_64b"
target_cpu: arm64
- arch: aarch64_generic-static
openwrt: "target=layerscape subtarget=armv8_64b"
target_cpu: arm64
extra: "build_static=true use_allocator_shim=false use_partition_alloc=false"
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: arm_arm1176jzf-s_vfp
openwrt: "target=brcm2708 subtarget=bcm2708"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="arm1176jzf-s" arm_fpu="vfp" arm_float_abi="hard" arm_use_neon=false arm_use_thumb=false'
- arch: arm_arm926ej-s
openwrt: "target=mxs subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="arm926ej-s" arm_float_abi="soft" arm_use_neon=false arm_use_thumb=false'
- arch: arm_cortex-a15_neon-vfpv4
openwrt: "target=ipq806x subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a15" arm_fpu="neon-vfpv4" arm_float_abi="hard" arm_use_neon=true'
- arch: arm_cortex-a5_vfpv4
openwrt: "target=at91 subtarget=sama5d3"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a5" arm_fpu="vfpv4" arm_float_abi="hard" arm_use_neon=false'
- arch: arm_cortex-a7
openwrt: "target=mediatek subtarget=mt7629"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a7" arm_float_abi="soft" arm_use_neon=false'
openwrt_release: '21.02.0'
openwrt_gcc_ver: '8.4.0'
- arch: arm_cortex-a7_neon-vfpv4
openwrt: "target=sunxi subtarget=cortexa7"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a7" arm_fpu="neon-vfpv4" arm_float_abi="hard" arm_use_neon=true'
- arch: arm_cortex-a7_neon-vfpv4-static
openwrt: "target=sunxi subtarget=cortexa7"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a7" arm_fpu="neon-vfpv4" arm_float_abi="hard" arm_use_neon=true build_static=true use_allocator_shim=false use_partition_alloc=false'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: arm_cortex-a7_vfpv4
openwrt: "target=at91 subtarget=sama7"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a7" arm_fpu="vfpv4" arm_float_abi="hard" arm_use_neon=false'
openwrt_release: '22.03.0'
openwrt_gcc_ver: '11.2.0'
- arch: arm_cortex-a8_vfpv3
openwrt: "target=sunxi subtarget=cortexa8"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a8" arm_fpu="vfpv3" arm_float_abi="hard" arm_use_neon=false'
- arch: arm_cortex-a9
openwrt: "target=bcm53xx subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a9" arm_float_abi="soft" arm_use_neon=false'
- arch: arm_cortex-a9-static
openwrt: "target=bcm53xx subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a9" arm_float_abi="soft" arm_use_neon=false build_static=true use_allocator_shim=false use_partition_alloc=false'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: arm_cortex-a9_neon
openwrt: "target=imx6 subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a9" arm_fpu="neon" arm_float_abi="hard" arm_use_neon=true'
- arch: arm_cortex-a9_vfpv3-d16
openwrt: "target=mvebu subtarget=cortexa9"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="cortex-a9" arm_fpu="vfpv3-d16" arm_float_abi="hard" arm_use_neon=false'
- arch: arm_mpcore
openwrt: "target=oxnas subtarget=ox820"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="mpcore" arm_float_abi="soft" arm_use_neon=false arm_use_thumb=false'
- arch: arm_xscale
openwrt: "target=kirkwood subtarget=generic"
target_cpu: arm
extra: 'arm_version=0 arm_cpu="xscale" arm_float_abi="soft" arm_use_neon=false arm_use_thumb=false'
- arch: mipsel_24kc
openwrt: "target=ramips subtarget=rt305x"
target_cpu: mipsel
extra: 'mips_arch_variant="r2" mips_float_abi="soft"'
- arch: mipsel_24kc-static
openwrt: "target=ramips subtarget=rt305x"
target_cpu: mipsel
extra: 'mips_arch_variant="r2" mips_float_abi="soft" build_static=true use_allocator_shim=false use_partition_alloc=false'
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
- arch: mipsel_mips32
openwrt: "target=brcm47xx subtarget=legacy"
target_cpu: mipsel
extra: 'mips_arch_variant="r1" mips_float_abi="soft"'
- arch: riscv64
openwrt: "target=sifiveu subtarget=generic"
target_cpu: riscv64
openwrt_release: '23.05.0'
openwrt_gcc_ver: '12.3.0'
- arch: loongarch64
openwrt: "target=loongarch64 subtarget=generic"
target_cpu: loong64
openwrt_release: '24.10.0'
openwrt_gcc_ver: '13.3.0'
env:
EXTRA_FLAGS: target_cpu="${{ matrix.target_cpu }}" target_os="openwrt" ${{ matrix.extra }}
OPENWRT_FLAGS: arch=${{ matrix.arch }} release=${{ matrix.openwrt_release || '18.06.0' }} gcc_ver=${{ matrix.openwrt_gcc_ver || '7.3.0' }} ${{ matrix.openwrt }}
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v4
with:
path: |
src/third_party/llvm-build/Release+Asserts/
src/gn/
src/qemu-user*.deb
key: toolchains-posix-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache PGO (Linux, OpenWrt)
uses: actions/cache@v4
with:
path: src/chrome/build/pgo_profiles/
key: pgo-linux-openwrt-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache sysroot
uses: actions/cache@v4
with:
path: src/out/sysroot-build/openwrt
key: sysroot-openwrt-23.05.0-${{ matrix.arch }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |
sudo apt update
sudo apt install ninja-build pkg-config ccache bubblewrap
sudo apt remove -y qemu-user-binfmt
sudo dpkg -i qemu-user*.deb
# libc6-i386 interferes with x86 build
sudo apt remove libc6-i386
- run: ./get-clang.sh
- run: ccache -z
- run: ./build.sh
- run: ccache -s && ccache --evict-older-than 1d
- run: ../tests/basic.sh out/Release/naive
- name: Pack naiveproxy assets
run: |
mkdir ${{ env.BUNDLE }}
cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
tar cJf ${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
openssl sha256 out/Release/naive >sha256sum.txt
echo "SHA256SUM=$(cut -d' ' -f2 sha256sum.txt)" >>$GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: ${{ env.BUNDLE }}.tar.xz naive executable sha256 ${{ env.SHA256SUM }}
path: src/sha256sum.txt
- name: Upload naiveproxy assets
if: ${{ github.event_name == 'release' }}
run: gh release upload "${GITHUB_REF##*/}" ${{ env.BUNDLE }}.tar.xz --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1 +1 @@
140.0.7339.123
143.0.7499.41

View File

@ -64,8 +64,12 @@ Options:
SOCKS-PROXY = "socks://"<HOSTNAME>[":"<PORT>]
Routes traffic via the proxy chain.
The default proxy is directly connection without proxying.
The default proxy is a direct connection without proxying.
The last PROXY-URI is negotiated automatically for Naive padding.
Can be specified multiple times, but they must match the number of specified
LISTEN-URIs, and each LISTEN-URI is routed to the PROXY matched by position.
Limitations:
* QUIC proxies cannot follow TCP-based proxies in a proxy chain.
* The user needs to ensure there is no loop in the proxy chain.
@ -89,6 +93,7 @@ Options:
--host-resolver-rules="MAP proxy.example.com 1.2.3.4"
Statically resolves a domain name to an IP address.
Multiple rules are comma separated.
--resolver-range=CIDR
@ -110,3 +115,8 @@ Options:
--no-post-quantum
Overrides the default and disables post-quantum key agreement.
--env=NAME=VALUE
Sets the environment variable NAME to the value VALUE. Can be specified
multiple times.

7
apk/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.gradle/
app/build/
app/libs/
build/
.idea/
local.properties
.kotlin/

73
apk/app/build.gradle.kts Normal file
View File

@ -0,0 +1,73 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "io.nekohasekai.sagernet.plugin.naive"
signingConfigs {
create("release") {
storeFile = rootProject.file("release.keystore")
storePassword = System.getenv("KEYSTORE_PASS")
keyAlias = "release"
keyPassword = System.getenv("KEYSTORE_PASS")
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
signingConfig = signingConfigs.getByName("release")
}
}
buildToolsVersion = "36.0.0"
compileSdk = 36
defaultConfig {
minSdk = 24
targetSdk = 36
applicationId = "io.nekohasekai.sagernet.plugin.naive"
versionCode = System.getenv("APK_VERSION_NAME").removePrefix("v").split(".")[0].toInt() * 10 + System.getenv("APK_VERSION_NAME").removePrefix("v").split("-")[1].toInt()
versionName = System.getenv("APK_VERSION_NAME").removePrefix("v")
splits.abi {
isEnable = true
isUniversalApk = false
reset()
include(System.getenv("APK_ABI"))
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
lint {
showAll = true
checkAllWarnings = true
checkReleaseBuilds = false
warningsAsErrors = true
}
packaging {
jniLibs.useLegacyPackaging = true
}
applicationVariants.all {
outputs.all {
this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
outputFileName =
outputFileName.replace(project.name, "naiveproxy-plugin-v$versionName")
.replace("-release", "")
.replace("-oss", "")
}
}
sourceSets.getByName("main") {
jniLibs.srcDir("libs")
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="internalOnly"
tools:ignore="MissingLeanbackLauncher">
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="Naïve Plugin"
android:roundIcon="@mipmap/ic_launcher_round">
<provider
android:name=".BinaryProvider"
android:authorities="io.nekohasekai.sagernet.plugin.naive.BinaryProvider"
android:directBootAware="true"
android:exported="true"
tools:ignore="ExportedContentProvider">
<intent-filter>
<action android:name="io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" />
</intent-filter>
<intent-filter>
<action android:name="io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" />
<data
android:host="io.nekohasekai.sagernet"
android:path="/naive-plugin"
android:scheme="plugin" />
</intent-filter>
<meta-data
android:name="io.nekohasekai.sagernet.plugin.id"
android:value="naive-plugin" />
<meta-data
android:name="io.nekohasekai.sagernet.plugin.executable_path"
android:value="libnaive.so" />
</provider>
</application>
</manifest>

View File

@ -0,0 +1,98 @@
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
******************************************************************************/
package io.nekohasekai.sagernet.plugin
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.os.Bundle
import android.os.ParcelFileDescriptor
abstract class NativePluginProvider : ContentProvider() {
override fun getType(uri: Uri): String? = "application/x-elf"
override fun onCreate(): Boolean = true
/**
* Provide all files needed for native plugin.
*
* @param provider A helper object to use to add files.
*/
protected abstract fun populateFiles(provider: PathProvider)
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?,
): Cursor? {
check(selection == null && selectionArgs == null && sortOrder == null)
val result = MatrixCursor(projection)
populateFiles(PathProvider(uri, result))
return result
}
/**
* Returns executable entry absolute path.
* This is used for fast mode initialization where ss-local launches your native binary at the path given directly.
* In order for this to work, plugin app is encouraged to have the following in its AndroidManifest.xml:
* - android:installLocation="internalOnly" for <manifest>
* - android:extractNativeLibs="true" for <application>
*
* Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the
* default behavior.
*
* @return Absolute path for executable entry.
*/
open fun getExecutable(): String = throw UnsupportedOperationException()
abstract fun openFile(uri: Uri): ParcelFileDescriptor
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor {
check(mode == "r")
return openFile(uri)
}
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? = when (method) {
PluginContract.METHOD_GET_EXECUTABLE -> {
Bundle().apply {
putString(PluginContract.EXTRA_ENTRY, getExecutable())
}
}
else -> super.call(method, arg, extras)
}
// Methods that should not be used
override fun insert(uri: Uri, values: ContentValues?): Uri? =
throw UnsupportedOperationException()
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?,
): Int =
throw UnsupportedOperationException()
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int =
throw UnsupportedOperationException()
}

View File

@ -0,0 +1,53 @@
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
******************************************************************************/
package io.nekohasekai.sagernet.plugin
import android.database.MatrixCursor
import android.net.Uri
import java.io.File
/**
* Helper class to provide relative paths of files to copy.
*/
class PathProvider internal constructor(baseUri: Uri, private val cursor: MatrixCursor) {
private val basePath = baseUri.path?.trim('/') ?: ""
fun addPath(path: String, mode: Int = 0b110100100): PathProvider {
val trimmed = path.trim('/')
if (trimmed.startsWith(basePath)) cursor.newRow()
.add(PluginContract.COLUMN_PATH, trimmed)
.add(PluginContract.COLUMN_MODE, mode)
return this
}
fun addTo(file: File, to: String = "", mode: Int = 0b110100100): PathProvider {
var sub = to + file.name
if (basePath.startsWith(sub)) if (file.isDirectory) {
sub += '/'
file.listFiles()!!.forEach { addTo(it, sub, mode) }
} else addPath(sub, mode)
return this
}
fun addAt(file: File, at: String = "", mode: Int = 0b110100100): PathProvider {
if (basePath.startsWith(at)) {
if (file.isDirectory) file.listFiles()!!.forEach { addTo(it, at, mode) } else addPath(at, mode)
}
return this
}
}

View File

@ -0,0 +1,34 @@
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
******************************************************************************/
package io.nekohasekai.sagernet.plugin
object PluginContract {
const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN"
const val EXTRA_ENTRY = "io.nekohasekai.sagernet.plugin.EXTRA_ENTRY"
const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id"
const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plguin.executable_path"
const val METHOD_GET_EXECUTABLE = "sagernet:getExecutable"
const val COLUMN_PATH = "path"
const val COLUMN_MODE = "mode"
const val SCHEME = "plugin"
const val AUTHORITY = "io.nekohasekai.sagernet"
}

View File

@ -0,0 +1,42 @@
/******************************************************************************
* *
* Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
******************************************************************************/
package io.nekohasekai.sagernet.plugin.naive
import android.net.Uri
import android.os.ParcelFileDescriptor
import io.nekohasekai.sagernet.plugin.NativePluginProvider
import io.nekohasekai.sagernet.plugin.PathProvider
import java.io.File
import java.io.FileNotFoundException
class BinaryProvider : NativePluginProvider() {
override fun populateFiles(provider: PathProvider) {
provider.addPath("naive-plugin", 0b111101101)
}
override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libnaive.so"
override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
"/naive-plugin" -> ParcelFileDescriptor.open(
File(getExecutable()),
ParcelFileDescriptor.MODE_READ_ONLY
)
else -> throw FileNotFoundException()
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108"
android:tint="#FFFFFF">
<group android:scaleX="0.13877095"
android:scaleY="0.13877095"
android:translateX="29.16"
android:translateY="42.010185">
<group android:translateY="138.67206">
<path android:pathData="M4.171875,-0L34.546875,-0L34.546875,-5.890625L25.90625,-7.046875L23.3125,-9.796875L23.3125,-82.125L78.1875,-0L87.6875,-0L87.6875,-87.328125L90.28125,-89.9375L98.921875,-91.234375L98.921875,-97L68.671875,-97L68.671875,-91.234375L77.3125,-89.9375L79.90625,-87.328125L79.90625,-19.34375L30.8125,-93.390625L30.8125,-97L3.734375,-97L3.734375,-91.234375L12.953125,-90.359375L15.546875,-88.34375L15.546875,-9.796875L12.953125,-7.046875L4.171875,-5.890625L4.171875,-0Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M151.09375,-8.375L152.95312,0L173.26562,0L173.26562,-5.171875L165.625,-5.90625L163.03125,-8.234375L163.03125,-50.3125C163.03125,-64.796875,156.70312,-71,141.57812,-71C123.4375,-71,112.5,-63.5,112.5,-54.75C112.5,-51.3125,113.640625,-50.3125,116.953125,-50.3125L126.890625,-50.3125L126.890625,-62.359375C130.78125,-63.9375,134.23438,-64.65625,137.84375,-64.65625C148.20312,-64.65625,151.09375,-59.921875,151.09375,-48.578125L151.09375,-44C122.28125,-37.0625,109.046875,-32.6875,109.046875,-17.546875C109.046875,-6.625,116.09375,1,127.46875,1C134.39062,1.015625,142.01562,-2.15625,151.09375,-8.375ZM151.09375,-13.328125C143.89062,-9.09375,137.98438,-6.765625,133.23438,-6.765625C126.03125,-6.765625,121.71875,-11.578125,121.71875,-18.703125C121.71875,-29.34375,130.78125,-33.265625,151.09375,-38.953125L151.09375,-13.328125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M197.59375,-70L181.03125,-65L181.03125,-60.53125L191.10938,-60.53125L191.10938,-8.203125L188.65625,-5.90625L181.03125,-5.171875L181.03125,0L213.4375,0L213.4375,-5.171875L205.65625,-5.90625L203.20312,-8.203125L203.20312,-70L197.59375,-70ZM182.76562,-99.53125C178.73438,-99.53125,175.42188,-96.375,175.42188,-92.1875C175.42188,-88.15625,178.73438,-85,182.76562,-85C186.79688,-85,189.95312,-88.15625,189.95312,-92.1875C189.95312,-96.375,186.79688,-99.53125,182.76562,-99.53125ZM209.54688,-99.53125C205.51562,-99.53125,202.20312,-96.375,202.20312,-92.1875C202.20312,-88.15625,205.51562,-85,209.54688,-85C213.57812,-85,216.75,-88.15625,216.75,-92.1875C216.75,-96.375,213.57812,-99.53125,209.54688,-99.53125Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M215.26562,-69L215.26562,-63.671875L224.0625,-62.796875L247.23438,0L255.29688,0L280.5,-62.515625L289,-63.671875L289,-69L263.51562,-69L263.51562,-63.671875L271.28125,-63.09375L272.4375,-60.796875L254,-13.90625L237.15625,-60.796875L238.89062,-63.09375L246.51562,-63.671875L246.51562,-69L215.26562,-69Z"
android:fillColor="#FFFFFF"/>
<path android:pathData="M351.5,-15.5C343.29688,-10.0625,336.09375,-7.203125,328.89062,-7.203125C314.35938,-7.203125,304.70312,-18.078125,304.70312,-36.421875C304.70312,-36.84375,304.70312,-37.421875,304.70312,-38L351.35938,-38C351.5,-39.578125,351.5,-41.015625,351.5,-42.453125C351.5,-60.296875,341.28125,-71,325.57812,-71C306.28125,-71,292.3125,-56.09375,292.3125,-34.265625C292.3125,-13.21875,305.42188,1,324.4375,1C333.5,1,342.85938,-1.875,351.5,-7.625L351.5,-15.5ZM338.6875,-44.046875L305.42188,-44.046875C306.28125,-56.375,314.5,-64.515625,323.71875,-64.515625C333.35938,-64.515625,338.6875,-58.125,338.6875,-46.359375C338.6875,-45.640625,338.6875,-44.765625,338.6875,-44.046875Z"
android:fillColor="#FFFFFF"/>
</group>
</group>
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#E91E63</color>
</resources>

18
apk/build.gradle Normal file
View File

@ -0,0 +1,18 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.11.0'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
tasks.register('clean', Delete) {
delete rootProject.layout.buildDirectory
}

21
apk/gradle.properties Normal file
View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

BIN
apk/gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

251
apk/gradlew vendored Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

94
apk/gradlew.bat vendored Normal file
View File

@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
apk/release.keystore Normal file

Binary file not shown.

10
apk/settings.gradle Normal file
View File

@ -0,0 +1,10 @@
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "Naive Plugin"
include ':app'

View File

@ -2165,7 +2165,7 @@ component("base") {
"process/set_process_title_linux.h",
"system/sys_info_linux.cc",
]
if (!is_cronet_build) {
if (!is_android) {
# These dependencies are not required on Android.
sources += [
"nix/mime_util_xdg.cc",

118
src/build.sh Executable file
View File

@ -0,0 +1,118 @@
#!/bin/sh
set -e
export TMPDIR="$PWD/tmp"
rm -rf "$TMPDIR"
mkdir -p "$TMPDIR"
if [ "$1" = debug ]; then
out=out/Debug
flags="
chrome_pgo_phase=0
is_debug=true
is_component_build=true"
else
out=out/Release
flags="
is_official_build=true
exclude_unwind_tables=true
enable_resource_allowlist_generation=false
chrome_pgo_phase=2
symbol_level=0"
fi
. ./get-sysroot.sh
# ccache
case "$host_os" in
linux|mac)
if which ccache >/dev/null 2>&1; then
export CCACHE_SLOPPINESS=time_macros
export CCACHE_BASEDIR="$PWD"
export CCACHE_CPP2=yes
CCACHE=ccache
fi
;;
win)
if [ -f "$HOME"/.cargo/bin/sccache* ]; then
export PATH="$PATH:$HOME/.cargo/bin"
CCACHE=sccache
fi
;;
esac
if [ "$CCACHE" ]; then
flags="$flags
cc_wrapper=\"$CCACHE\""
fi
flags="$flags"'
is_clang=true
use_sysroot=false
fatal_linker_warnings=false
treat_warnings_as_errors=false
is_cronet_build=true
use_udev=false
use_aura=false
use_ozone=false
use_gio=false
use_platform_icu_alternatives=true
use_glib=false
is_perfetto_embedder=true
disable_file_support=true
enable_websockets=false
use_kerberos=false
disable_file_support=true
disable_zstd_filter=false
enable_mdns=false
enable_reporting=false
include_transport_security_state_preload_list=false
enable_device_bound_sessions=false
enable_bracketed_proxy_uris=true
enable_quic_proxy_support=true
enable_disk_cache_sql_backend=false
use_nss_certs=false
enable_backup_ref_ptr_support=false
enable_dangling_raw_ptr_checks=false
'
if [ "$WITH_SYSROOT" ]; then
flags="$flags
target_sysroot=\"//$WITH_SYSROOT\""
fi
if [ "$host_os" = "mac" ]; then
flags="$flags"'
enable_dsyms=false'
fi
case "$EXTRA_FLAGS" in
*target_os=\"android\"*)
# default_min_sdk_version=24: 26 introduces unnecessary snew symbols
# is_high_end_android=true: Does not optimize for size, Uses PGO profiles
flags="$flags"'
default_min_sdk_version=24
is_high_end_android=true'
;;
esac
# See https://github.com/llvm/llvm-project/issues/86430
if [ "$target_os" = "linux" -a "$target_cpu" = "x64" ]; then
flags="$flags"'
use_cfi_icall=false'
fi
rm -rf "./$out"
mkdir -p out
export DEPOT_TOOLS_WIN_TOOLCHAIN=0
./gn/out/gn gen "$out" --args="$flags $EXTRA_FLAGS"
ninja -C "$out" naive

19
src/get-android-sys.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -ex
. ./get-sysroot.sh
if [ "$WITH_ANDROID_IMG" -a ! -d out/sysroot-build/android/"$WITH_ANDROID_IMG"/system ]; then
curl -O https://dl.google.com/android/repository/sys-img/android/$WITH_ANDROID_IMG.zip
mkdir -p $WITH_ANDROID_IMG/mount
unzip $WITH_ANDROID_IMG.zip '*/system.img' -d $WITH_ANDROID_IMG
# Need mount -t ext4 -o ro,loop,offset=0x100000 for API level of 26+
sudo mount $WITH_ANDROID_IMG/*/system.img $WITH_ANDROID_IMG/mount
rootfs=out/sysroot-build/android/$WITH_ANDROID_IMG
mkdir -p $rootfs/system/bin $rootfs/system/etc
cp $WITH_ANDROID_IMG/mount/bin/linker* $rootfs/system/bin
cp $WITH_ANDROID_IMG/mount/etc/hosts $rootfs/system/etc
cp -r $WITH_ANDROID_IMG/mount/lib* $rootfs/system
sudo umount $WITH_ANDROID_IMG/mount
rm -rf $WITH_ANDROID_IMG $WITH_ANDROID_IMG.zip
fi

117
src/get-clang.sh Executable file
View File

@ -0,0 +1,117 @@
#!/bin/sh
set -ex
. ./get-sysroot.sh
if [ "$SYSROOT_ARCH" -a ! -d ./"$WITH_SYSROOT/lib" ]; then
./build/linux/sysroot_scripts/sysroot_creator.py build "$SYSROOT_ARCH"
fi
if [ "$OPENWRT_FLAGS" ]; then
./get-openwrt.sh
fi
# Clang
# See src/tools/clang/scripts/update.py
case "$host_os" in
linux) WITH_CLANG=Linux_x64;;
win) WITH_CLANG=Win;;
mac) WITH_CLANG=Mac;;
esac
if [ "$host_os" = mac -a "$host_cpu" = arm64 ]; then
WITH_CLANG=Mac_arm64
fi
mkdir -p third_party/llvm-build/Release+Asserts
cd tools/clang/scripts
CLANG_REVISION=$($PYTHON -c 'import update; print(update.PACKAGE_VERSION)')
cd -
echo $CLANG_REVISION >third_party/llvm-build/Release+Asserts/cr_build_revision
if [ ! -d third_party/llvm-build/Release+Asserts/bin ]; then
mkdir -p third_party/llvm-build/Release+Asserts
clang_path="clang-$CLANG_REVISION.tar.xz"
clang_url="https://commondatastorage.googleapis.com/chromium-browser-clang/$WITH_CLANG/$clang_path"
curl "$clang_url" | tar xJf - -C third_party/llvm-build/Release+Asserts
fi
# sccache
if [ "$host_os" = win -a ! -f ~/.cargo/bin/sccache.exe ]; then
sccache_url="https://github.com/mozilla/sccache/releases/download/0.2.12/sccache-0.2.12-x86_64-pc-windows-msvc.tar.gz"
mkdir -p ~/.cargo/bin
curl -L "$sccache_url" | tar xzf - --strip=1 -C ~/.cargo/bin
fi
# GN
# See src/DEPS
case "$host_os" in
linux) WITH_GN=linux-amd64;;
win) WITH_GN=windows-amd64;;
mac) WITH_GN=mac-amd64;;
esac
if [ "$host_os" = mac -a "$host_cpu" = arm64 ]; then
WITH_GN=mac-arm64
fi
if [ ! -f gn/out/gn ]; then
gn_version=$(grep "'gn_version':" DEPS | cut -d"'" -f4)
mkdir -p gn/out
curl -L "https://chrome-infra-packages.appspot.com/dl/gn/gn/$WITH_GN/+/$gn_version" -o gn.zip
unzip gn.zip -d gn/out
rm gn.zip
fi
# See src/build/config/compiler/pgo/BUILD.gn
case "$target_os" in
win)
case "$target_cpu" in
arm64) WITH_PGO=win-arm64;;
x64) WITH_PGO=win64;;
*) WITH_PGO=win32;;
esac
;;
mac)
case "$target_cpu" in
arm64) WITH_PGO=mac-arm;;
*) WITH_PGO=mac;;
esac
;;
linux|openwrt)
WITH_PGO=linux
;;
android)
case "$target_cpu" in
arm64) WITH_PGO=android-arm64;;
*) WITH_PGO=android-arm32;;
esac
;;
esac
if [ "$WITH_PGO" ]; then
PGO_PATH=$(cat chrome/build/$WITH_PGO.pgo.txt)
fi
if [ "$WITH_PGO" -a ! -f chrome/build/pgo_profiles/"$PGO_PATH" ]; then
mkdir -p chrome/build/pgo_profiles
cd chrome/build/pgo_profiles
curl --limit-rate 10M -LO "https://storage.googleapis.com/chromium-optimization-profiles/pgo_profiles/$PGO_PATH"
cd ../../..
fi
if [ "$target_os" = android -a ! -d third_party/android_toolchain/ndk ]; then
# https://dl.google.com/android/repository/android-ndk-r25c-linux.zip
android_ndk_version=$(grep 'default_android_ndk_version = ' build/config/android/config.gni | cut -d'"' -f2)
curl -LO https://dl.google.com/android/repository/android-ndk-$android_ndk_version-linux.zip
unzip android-ndk-$android_ndk_version-linux.zip
mkdir -p third_party/android_toolchain/ndk
cd android-ndk-$android_ndk_version
cp -r --parents sources/android/cpufeatures ../third_party/android_toolchain/ndk
cp -r --parents toolchains/llvm/prebuilt ../third_party/android_toolchain/ndk
cd ..
cd third_party/android_toolchain/ndk
find toolchains -type f -regextype egrep \! -regex \
'.*(lib(atomic|gcc|gcc_real|compiler_rt-extras|android_support|unwind).a|crt.*o|lib(android|c|dl|log|m).so|usr/local.*|usr/include.*)' -delete
# Works around https://github.com/android/ndk/issues/2082
# .../android/hardware_buffer.h:322:42: error: expression is not an integral constant expression
# 322 | AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32,
# .../android/hardware_buffer.h:322:46: note: shift count 32 >= width of type 'unsigned long' (32 bits)
# 322 | AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32,
sed -i 's/AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL /AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1ULL /' toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/android/hardware_buffer.h
cd -
rm -rf android-ndk-$android_ndk_version android-ndk-$android_ndk_version-linux.zip
fi

111
src/get-openwrt.sh Executable file
View File

@ -0,0 +1,111 @@
#!/bin/sh
set -ex
eval "$OPENWRT_FLAGS"
sysroot=$PWD/out/sysroot-build/openwrt/$release/$arch
if [ -d $sysroot/lib ]; then
exit 0
fi
mkdir -p $sysroot
case "$arch" in
arm_*) abi=musl_eabi;;
*) abi=musl;;
esac
major=${release%%.*}
if [ "$major" -ge 22 ]; then
path_suffix=toolchain
else
path_suffix=sdk
fi
if [ "$major" -ge 24 ]; then
tarball_suffix=zst
else
tarball_suffix=xz
fi
if [ ! "$subtarget" ]; then
subtarget=generic
fi
if [ "$subtarget" != generic -o "$major" -ge 22 ]; then
SDK_PATH=openwrt-$path_suffix-$release-$target-${subtarget}_gcc-${gcc_ver}_${abi}.Linux-x86_64
else
SDK_PATH=openwrt-$path_suffix-$release-${target}_gcc-${gcc_ver}_${abi}.Linux-x86_64
fi
SDK_URL=https://downloads.openwrt.org/releases/$release/targets/$target/$subtarget/$SDK_PATH.tar.$tarball_suffix
rm -rf $SDK_PATH
if [ $tarball_suffix = xz ]; then
curl $SDK_URL | tar xJf -
elif [ $tarball_suffix = zst ]; then
curl $SDK_URL | tar --zstd -xf -
fi
full_root=toolchain-*_gcc-${gcc_ver}_${abi}
if [ "$major" -lt 22 ]; then
mv $SDK_PATH/staging_dir/$full_root $SDK_PATH
fi
cd $SDK_PATH
cat >include.txt <<EOF
./include
./lib/*.o
./lib/gcc/*/libgcc.a
./lib/gcc/*/libgcc_eh.a
./lib/libatomic.so*
./lib/libatomic.a
./lib/libc.so
./lib/libc.a
./lib/libdl.a
./lib/ld-*
./lib/libgcc_s.*
./lib/libm.a
./lib/libpthread.a
./lib/libresolv.a
./lib/librt.a
./usr
EOF
tar cf - -C $full_root --hard-dereference . | tar xf - -C $sysroot --wildcards --wildcards-match-slash -T include.txt
rm -rf include.txt
cd ..
rm -rf $SDK_PATH
# LLVM does not accept muslgnueabi as the target triple environment
if [ -d "$sysroot/lib/gcc/arm-openwrt-linux-muslgnueabi" ]; then
mv "$sysroot/lib/gcc/arm-openwrt-linux-muslgnueabi" "$sysroot/lib/gcc/arm-openwrt-linux-musleabi"
fi
if [ "$arch" = "loongarch64" ]; then
if grep HWCAP_LOONGARCH_LSX "$sysroot/include/bits/hwcap.h"; then
echo "$sysroot/include/bits/hwcap.h" is already populated
exit 1
fi
# https://www.openwall.com/lists/musl/2025/04/03/3
cat >"$sysroot/include/bits/hwcap.h" <<EOF
/* The following must match the kernel's <asm/hwcap.h>. */
/* HWCAP flags */
#define HWCAP_LOONGARCH_CPUCFG (1 << 0)
#define HWCAP_LOONGARCH_LAM (1 << 1)
#define HWCAP_LOONGARCH_UAL (1 << 2)
#define HWCAP_LOONGARCH_FPU (1 << 3)
#define HWCAP_LOONGARCH_LSX (1 << 4)
#define HWCAP_LOONGARCH_LASX (1 << 5)
#define HWCAP_LOONGARCH_CRC32 (1 << 6)
#define HWCAP_LOONGARCH_COMPLEX (1 << 7)
#define HWCAP_LOONGARCH_CRYPTO (1 << 8)
#define HWCAP_LOONGARCH_LVZ (1 << 9)
#define HWCAP_LOONGARCH_LBT_X86 (1 << 10)
#define HWCAP_LOONGARCH_LBT_ARM (1 << 11)
#define HWCAP_LOONGARCH_LBT_MIPS (1 << 12)
#define HWCAP_LOONGARCH_PTW (1 << 13)
EOF
# https://www.openwall.com/lists/musl/2022/03/22/4
# But this breaks C++.
sed -i 's/extcontext\[\] /extcontext\[0\] /' "$sysroot/include/bits/signal.h"
fi

72
src/get-sysroot.sh Normal file
View File

@ -0,0 +1,72 @@
eval "$EXTRA_FLAGS"
case "$(uname)" in
MINGW*|MSYS*) host_os=win;;
Linux) host_os=linux;;
Darwin) host_os=mac;;
*) echo "Unsupported host OS" >&2; exit 1;;
esac
case "$(uname -m)" in
x86_64|x64) host_cpu=x64;;
x86|i386|i686) host_cpu=x86;;
arm) host_cpu=arm;;
arm64|aarch64|armv8b|armv8l) host_cpu=arm64;;
*) echo "Unsupported host CPU" >&2; exit 1;;
esac
# See src/build/config/BUILDCONFIG.gn
if [ ! "$target_os" ]; then
target_os="$host_os"
fi
if [ ! "$target_cpu" ]; then
target_cpu="$host_cpu"
fi
if which python3 >/dev/null 2>/dev/null; then
PYTHON=python3
else
PYTHON=python
fi
# sysroot
case "$target_os" in
linux)
RELEASE=bullseye
case "$target_cpu" in
x64) SYSROOT_ARCH=amd64;;
x86) SYSROOT_ARCH=i386;;
arm64) SYSROOT_ARCH=arm64;;
arm) SYSROOT_ARCH=armhf;;
mipsel) SYSROOT_ARCH=mipsel;;
mips64el) SYSROOT_ARCH=mips64el;;
riscv64) SYSROOT_ARCH=riscv64; RELEASE=trixie;;
loong64) SYSROOT_ARCH=loong64; RELEASE=sid;;
esac
if [ "$SYSROOT_ARCH" ]; then
WITH_SYSROOT="out/sysroot-build/${RELEASE}/${RELEASE}_${SYSROOT_ARCH}_staging"
fi
# This is the case where running ./build.sh without EXTRA_FLAGS
# wants to avoid downloading sysroots.
if [ ! "$EXTRA_FLAGS" -a "$target_cpu" = "$host_cpu" ]; then
SYSROOT_ARCH=
WITH_SYSROOT=
fi
;;
openwrt)
eval "$OPENWRT_FLAGS"
WITH_SYSROOT="out/sysroot-build/openwrt/$release/$arch"
;;
android)
WITH_SYSROOT=
# https://dl.google.com/android/repository/sys-img/android/sys-img.xml
# https://dl.google.com/android/repository/sys-img/google_apis/sys-img.xml
case "$target_cpu" in
x64) WITH_ANDROID_IMG=x86_64-24_r08;;
x86) WITH_ANDROID_IMG=x86-24_r08;;
arm64) WITH_ANDROID_IMG=arm64-v8a-24_r07;;
arm) WITH_ANDROID_IMG=armeabi-v7a-24_r07;;
esac
;;
esac

View File

@ -14,6 +14,7 @@
#include "build/build_config.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/base/net_export.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/dns/public/secure_dns_policy.h"
@ -228,7 +229,7 @@ base::TimeDelta ClientSocketPoolManager::unused_idle_socket_timeout(
return base::Seconds(kPreconnectIntervalSec);
}
int InitSocketHandleForHttpRequest(
int NET_EXPORT InitSocketHandleForHttpRequest(
url::SchemeHostPort endpoint,
int request_load_flags,
RequestPriority request_priority,

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include <iostream>
#include "base/environment.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
@ -76,17 +77,13 @@ NaiveConfig::~NaiveConfig() = default;
bool NaiveConfig::Parse(const base::Value::Dict& value) {
if (const base::Value* v = value.Find("listen")) {
listen.clear();
std::vector<std::string> listen_strs;
if (const std::string* str = v->GetIfString()) {
if (!listen.emplace_back().Parse(*str)) {
return false;
}
listen_strs.push_back(*str);
} else if (const base::Value::List* strs = v->GetIfList()) {
for (const auto& str_e : *strs) {
if (const std::string* s = str_e.GetIfString()) {
if (!listen.emplace_back().Parse(*s)) {
return false;
}
listen_strs.push_back(*s);
} else {
std::cerr << "Invalid listen element" << std::endl;
return false;
@ -96,6 +93,14 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
std::cerr << "Invalid listen" << std::endl;
return false;
}
if (!listen_strs.empty()) {
listen.clear();
}
for (const std::string& str : listen_strs) {
if (!listen.emplace_back().Parse(str)) {
return false;
}
}
}
if (const base::Value* v = value.Find("insecure-concurrency")) {
@ -126,8 +131,24 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
}
if (const base::Value* v = value.Find("proxy")) {
std::vector<std::string> proxy_strs;
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
base::StringTokenizer proxy_uri_list(*str, ",");
proxy_strs.push_back(*str);
} else if (const base::Value::List* strs = v->GetIfList()) {
for (const auto& str_e : *strs) {
if (const std::string* s = str_e.GetIfString(); s && !s->empty()) {
proxy_strs.push_back(*s);
} else {
std::cerr << "Invalid proxy element" << std::endl;
return false;
}
}
} else {
std::cerr << "Invalid proxy argument" << std::endl;
return false;
}
for (const std::string& str : proxy_strs) {
base::StringTokenizer proxy_uri_list(str, ",");
std::vector<ProxyServer> proxy_servers;
bool seen_tcp = false;
while (proxy_uri_list.GetNext()) {
@ -187,6 +208,7 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
<< std::endl;
return false;
}
ProxyChain proxy_chain;
if (std::any_of(proxy_servers.begin(), proxy_servers.end(),
[](const ProxyServer& s) { return s.is_quic(); })) {
proxy_chain = ProxyChain::ForIpProtection(proxy_servers);
@ -198,9 +220,7 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
std::cerr << "Invalid proxy chain" << std::endl;
return false;
}
} else {
std::cerr << "Invalid proxy argument" << std::endl;
return false;
proxy_chains.push_back(proxy_chain);
}
}
@ -266,6 +286,39 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
no_post_quantum = true;
}
if (const base::Value* v = value.Find("env")) {
std::vector<std::string> env_strs;
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
env_strs.push_back(*str);
} else if (const base::Value::List* strs = v->GetIfList()) {
for (const auto& str_e : *strs) {
if (const std::string* s = str_e.GetIfString(); s && !s->empty()) {
env_strs.push_back(*s);
} else {
std::cerr << "Invalid env element" << std::endl;
return false;
}
}
} else {
std::cerr << "Invalid env argument" << std::endl;
return false;
}
auto env = base::Environment::Create();
for (const std::string& str : env_strs) {
size_t equal_pos = str.find_first_of('=');
if (equal_pos != std::string::npos && equal_pos > 0 &&
equal_pos + 1 < str.size()) {
std::string env_name = str.substr(0, equal_pos);
std::string env_value = str.substr(equal_pos + 1);
if (!env->SetVar(env_name, env_value)) {
std::cerr << "Invalid env element " << str << std::endl;
}
} else {
std::cerr << "Invalid env element " << str << std::endl;
}
}
}
return true;
}

View File

@ -44,7 +44,7 @@ struct NaiveConfig {
HttpRequestHeaders extra_headers;
// The last server is assumed to be Naive.
ProxyChain proxy_chain = ProxyChain::Direct();
std::vector<ProxyChain> proxy_chains;
std::set<HostPortPair> origins_to_force_quic_on;
std::map<url::SchemeHostPort, AuthCredentials> auth_store;

View File

@ -13,6 +13,7 @@
#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
@ -172,7 +173,8 @@ std::unique_ptr<URLRequestContext> BuildCertURLRequestContext(NetLog* net_log) {
std::unique_ptr<URLRequestContext> BuildURLRequestContext(
const NaiveConfig& config,
scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher,
NetLog* net_log) {
NetLog* net_log,
int proxy_chain_index = 0) {
URLRequestContextBuilder builder;
builder.DisableHttpCache();
@ -212,8 +214,13 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
ProxyConfig proxy_config;
proxy_config.proxy_rules().type =
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
config.proxy_chain);
if (config.proxy_chains.empty()) {
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
ProxyChain::Direct());
} else {
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
config.proxy_chains.at(proxy_chain_index));
}
LOG(INFO) << "Proxying via "
<< proxy_config.proxy_rules().single_proxies.ToDebugString();
auto proxy_service =
@ -456,14 +463,20 @@ int main(int argc, char* argv[]) {
cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
cert_net_fetcher->SetURLRequestContext(cert_context.get());
#endif
auto context =
net::BuildURLRequestContext(config, std::move(cert_net_fetcher), net_log);
auto* session = context->http_transaction_factory()->GetSession();
if (config.proxy_chains.size() >= 2 &&
config.proxy_chains.size() != config.listen.size()) {
std::cerr << "Listen addresses do not match multiple proxy chains"
<< std::endl;
return EXIT_FAILURE;
}
std::vector<std::unique_ptr<net::NaiveProxy>> naive_proxies;
std::vector<std::unique_ptr<net::URLRequestContext>> contexts;
std::unique_ptr<net::RedirectResolver> resolver;
for (const net::NaiveListenConfig& listen_config : config.listen) {
for (size_t listen_i = 0; listen_i < config.listen.size(); ++listen_i) {
const net::NaiveListenConfig& listen_config = config.listen[listen_i];
auto listen_socket =
std::make_unique<net::TCPServerSocket>(net_log, net::NetLogSource());
@ -503,6 +516,15 @@ int main(int argc, char* argv[]) {
config.resolver_prefix);
}
if (config.proxy_chains.size() >= 2) {
contexts.push_back(net::BuildURLRequestContext(
config, std::move(cert_net_fetcher), net_log, listen_i));
} else if (contexts.empty()) {
contexts.push_back(net::BuildURLRequestContext(
config, std::move(cert_net_fetcher), net_log));
}
auto& context = contexts.back();
auto* session = context->http_transaction_factory()->GetSession();
auto naive_proxy = std::make_unique<net::NaiveProxy>(
std::move(listen_socket), listen_config.protocol, listen_config.user,
listen_config.pass, config.insecure_concurrency, resolver.get(),
@ -512,6 +534,9 @@ int main(int argc, char* argv[]) {
naive_proxies.push_back(std::move(naive_proxy));
}
if (base::Environment::Create()->HasVar("TEST_MARK_STARTUP")) {
LOG(INFO) << "TEST_MARK_STARTUP";
}
base::RunLoop().Run();
return EXIT_SUCCESS;

View File

@ -105,12 +105,6 @@ def _get_profile_path(args):
"""
if args.override_filename:
profile_path = os.path.join(_PGO_PROFILE_DIR, args.override_filename)
elif args.target == 'android-arm64':
# By default on android for arm64 we use the PGO profile that is generated
# at the same commit as the orderfile. See https://crbug.com/372686816 for
# more context on why this is necessary.
profile_path = os.path.join(_ANDROID_ARM64_PROFILE_DIR,
'pgo_profile.arm64.profdata')
else:
profile_path = os.path.join(_PGO_PROFILE_DIR,
_read_profile_name(args.target))

332
tests/basic.py Normal file
View File

@ -0,0 +1,332 @@
#!/usr/bin/env python3
import argparse
import http.server
import os
import sys
import shutil
import ssl
import subprocess
import tempfile
import threading
import time
parser = argparse.ArgumentParser()
parser.add_argument('--naive', required=True)
parser.add_argument('--rootfs')
parser.add_argument('--target_cpu')
parser.add_argument('--server_protocol',
choices=['http', 'https'], default='https')
argv = parser.parse_args()
if argv.rootfs:
try:
os.remove(os.path.join(argv.rootfs, 'naive'))
except OSError:
pass
server_protocol = argv.server_protocol
_, certfile = tempfile.mkstemp()
result = subprocess.run(
f'openssl req -new -x509 -keyout {certfile} -out {certfile} -days 1 -nodes -subj /C=XX'.split(), capture_output=True)
result.check_returncode()
HTTPS_SERVER_HOSTNAME = '127.0.0.1'
HTTP_SERVER_PORT = 60443 if server_protocol == 'https' else 60080
httpd = http.server.HTTPServer(
(HTTPS_SERVER_HOSTNAME, HTTP_SERVER_PORT), http.server.SimpleHTTPRequestHandler)
httpd.timeout = 1
httpd.allow_reuse_address = True
if server_protocol == 'https':
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile=certfile)
httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True)
httpd_thread = threading.Thread(
target=lambda httpd: httpd.serve_forever(), args=(httpd,), daemon=True)
httpd_thread.start()
def test_https_server(hostname, port, proxy=None):
url = f'{server_protocol}://{hostname}:{port}/404'
cmdline = ['curl', '-k', '-s']
if proxy:
cmdline.extend(['--proxy', proxy])
cmdline.append(url)
print('subprocess.run', ' '.join(cmdline))
result = subprocess.run(cmdline, capture_output=True,
timeout=10, text=True, encoding='utf-8')
print(result.stderr, end='')
return 'Error code: 404' in result.stdout
assert test_https_server(HTTPS_SERVER_HOSTNAME,
HTTP_SERVER_PORT), 'https server not up'
def start_naive(naive_args, config_file):
with_qemu = None
if argv.target_cpu == 'arm64':
with_qemu = 'aarch64'
elif argv.target_cpu == 'arm':
with_qemu = 'arm'
elif argv.target_cpu == 'mipsel':
with_qemu = 'mipsel'
elif argv.target_cpu == 'mips64el':
with_qemu = 'mips64el'
elif argv.target_cpu == 'riscv64':
with_qemu = 'riscv64'
elif argv.target_cpu == 'loong64':
with_qemu = 'loong64'
if argv.rootfs:
if not with_qemu:
if not os.path.exists(os.path.join(argv.rootfs, 'naive')):
shutil.copy2(argv.naive, argv.rootfs)
# bwrap isolates filesystem, config_file needs a copy inside.
if config_file is not None:
shutil.copy2(config_file, argv.rootfs)
cmdline = ['bwrap', '--die-with-parent', '--bind', argv.rootfs, '/',
'--proc', '/proc', '--dev', '/dev', '--chdir', '/', '/naive']
else:
cmdline = [f'qemu-{with_qemu}',
'-L', argv.rootfs, argv.naive]
else:
cmdline = [argv.naive]
cmdline.extend(naive_args)
env = os.environ.copy()
env["TEST_MARK_STARTUP"] = "yes"
proc = subprocess.Popen(cmdline, stdout=subprocess.DEVNULL, env=env,
stderr=subprocess.PIPE, text=True, encoding='utf-8')
print('subprocess.Popen', ' '.join(cmdline), 'pid:', proc.pid)
def terminate(proc):
print('proc has timed out')
print('terminate pid', proc.pid)
proc.terminate()
# Some qemu-user tests take a while to start.
# See https://gitlab.com/qemu-project/qemu/-/issues/1729
timeout = threading.Timer(20, terminate, args=(proc,))
timeout.start()
while True:
if proc.poll() is not None:
timeout.cancel()
return proc.poll() == 0
line = proc.stderr.readline().strip()
print(line)
if 'Failed to listen' in line:
timeout.cancel()
print('terminate pid', proc.pid)
proc.terminate()
return 'Failed to listen'
elif 'TEST_MARK_STARTUP' in line:
timeout.cancel()
return proc
port = 10000
def allocate_port_number():
global port
port += 1
if port > 60000:
port = 10000
return port
def test_naive_once(proxy, *args, **kwargs):
port_map = {}
class PortDict(dict):
def __init__(self, port_map):
self._port_map = port_map
def __getitem__(self, key):
if key.startswith('PORT'):
if key not in self._port_map:
self._port_map[key] = str(allocate_port_number())
return self._port_map[key]
return key
port_dict = PortDict(port_map)
proxy = proxy.format_map(port_dict)
config_file = kwargs.get('config_file')
config_content = kwargs.get('config_content')
if config_content is not None:
config_content = config_content.format_map(port_dict)
print(f"Writing {repr(config_content)} to {config_file}")
with open(config_file, 'w') as f:
f.write('{')
f.write(config_content)
f.write('}')
naive_procs = []
def cleanup():
if config_file is not None:
os.remove(config_file)
for naive_proc in naive_procs:
print('terminate pid', naive_proc.pid)
naive_proc.terminate()
for args_instance in args:
naive_args = args_instance.format_map(port_dict).split()
naive_proc = start_naive(naive_args, config_file)
if naive_proc == 'Failed to listen':
cleanup()
return 'Failed to listen'
if not naive_proc:
cleanup()
return False
naive_procs.append(naive_proc)
result = test_https_server(HTTPS_SERVER_HOSTNAME, HTTP_SERVER_PORT, proxy)
cleanup()
return result
def test_naive(label, proxy, *args, **kwargs):
RETRIES = 5
result = None
if argv.target_cpu == 'arm' and not label.startswith('Default'):
# Arm tests are too slow in qemu-user
# due to https://www.openwall.com/lists/musl/2017/06/15/9
print('** SKIP TEST:', label, end='\n\n')
return
for i in range(RETRIES):
result = test_naive_once(proxy, *args, **kwargs)
if result == 'Failed to listen':
result = False
print('Retrying...')
time.sleep(1)
continue
break
if result is True:
print('** TEST PASS:', label, end='\n\n')
else:
print('** TEST FAIL:', label, end='\n\n')
sys.exit(1)
test_naive('Default config', 'socks5h://127.0.0.1:1080',
'--log')
test_naive('Default config file', 'socks5h://127.0.0.1:{PORT1}',
'',
config_content='"listen":"socks://127.0.0.1:{PORT1}","log":""',
config_file='config.json')
test_naive('Custom config file', 'socks5h://127.0.0.1:{PORT1}',
'custom.json',
config_content='"listen":"socks://127.0.0.1:{PORT1}","log":""',
config_file='custom.json')
test_naive('Multiple listens - command line', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --listen=http://:{PORT2}')
test_naive('Multiple listens - command line', 'http://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT1} --listen=http://:{PORT2}')
test_naive('Multiple listens - config file', 'socks5h://127.0.0.1:{PORT1}',
'multiple-listen.json',
config_content='"listen":["socks://:{PORT1}", "http://:{PORT2}"],"log":""',
config_file='multiple-listen.json')
test_naive('Multiple listens - config file', 'http://127.0.0.1:{PORT2}',
'multiple-listen.json',
config_content='"listen":["socks://:{PORT1}", "http://:{PORT2}"],"log":""',
config_file='multiple-listen.json')
test_naive('Multiple proxies - command line', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --listen=socks://:{PORT2} --proxy=http://127.0.0.1:{PORT3} --proxy=http://127.0.0.1:{PORT4}',
'--log --listen=http://:{PORT3} --listen=http://:{PORT4} --proxy=socks://127.0.0.1:{PORT5}',
'--log --listen=socks://:{PORT5}')
test_naive('Multiple proxies - command line', 'socks5h://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT1} --listen=socks://:{PORT2} --proxy=http://127.0.0.1:{PORT3} --proxy=http://127.0.0.1:{PORT4}',
'--log --listen=http://:{PORT3} --listen=http://:{PORT4} --proxy=socks://127.0.0.1:{PORT5}',
'--log --listen=socks://:{PORT5}')
test_naive('Trivial - listen scheme only', 'socks5h://127.0.0.1:1080',
'--log --listen=socks://')
test_naive('Trivial - listen no host', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1}')
test_naive('Trivial - listen no port', 'socks5h://127.0.0.1:1080',
'--log --listen=socks://127.0.0.1')
test_naive('Trivial - auth', 'socks5h://user:pass@127.0.0.1:{PORT1}',
'--log --listen=socks://user:pass@127.0.0.1:{PORT1}')
test_naive('Trivial - auth with special chars', 'socks5h://user:^@127.0.0.1:{PORT1}',
'--log --listen=socks://user:^@127.0.0.1:{PORT1}')
test_naive('Trivial - auth with special chars', 'socks5h://^:^@127.0.0.1:{PORT1}',
'--log --listen=socks://^:^@127.0.0.1:{PORT1}')
test_naive('Trivial - auth with empty pass', 'socks5h://user:@127.0.0.1:{PORT1}',
'--log --listen=socks://user:@127.0.0.1:{PORT1}')
test_naive('SOCKS-SOCKS', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --proxy=socks://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT2}')
test_naive('SOCKS-SOCKS - proxy no port', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --proxy=socks://127.0.0.1',
'--log --listen=socks://:1080')
test_naive('SOCKS-HTTP', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --proxy=http://127.0.0.1:{PORT2}',
'--log --listen=http://:{PORT2}')
test_naive('HTTP-HTTP', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=http://127.0.0.1:{PORT2}',
'--log --listen=http://:{PORT2}')
test_naive('HTTP-SOCKS', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=socks://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT2}')
test_naive('SOCKS-SOCKS-SOCKS', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --proxy=socks://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT2} --proxy=socks://127.0.0.1:{PORT3}',
'--log --listen=socks://:{PORT3}')
test_naive('SOCKS-HTTP-SOCKS', 'socks5h://127.0.0.1:{PORT1}',
'--log --listen=socks://:{PORT1} --proxy=http://127.0.0.1:{PORT2}',
'--log --listen=http://:{PORT2} --proxy=socks://127.0.0.1:{PORT3}',
'--log --listen=socks://:{PORT3}')
test_naive('HTTP-SOCKS-HTTP', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=socks://127.0.0.1:{PORT2}',
'--log --listen=socks://:{PORT2} --proxy=http://127.0.0.1:{PORT3}',
'--log --listen=http://:{PORT3}')
test_naive('HTTP-HTTP-HTTP', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=http://127.0.0.1:{PORT2}',
'--log --listen=http://:{PORT2} --proxy=http://127.0.0.1:{PORT3}',
'--log --listen=http://:{PORT3}')
test_naive('HTTP-HTTP (with auth)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=http://hello:world@127.0.0.1:{PORT2}',
'--log --listen=http://hello:world@127.0.0.1:{PORT2}')
test_naive('HTTPa-HTTPb,HTTPc (chaining with remote loop)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT2}',
'--log --listen=http://:{PORT1} --proxy=http://127.0.0.1:{PORT2},http://127.0.0.1:{PORT2}')
test_naive('HTTPa-HTTPb,HTTPc (chaining with multiple auth)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://hello:world2@127.0.0.1:{PORT2}',
'--log --listen=http://hello:world3@127.0.0.1:{PORT3}',
'--log --listen=http://127.0.0.1:{PORT1} --proxy=http://hello:world2@127.0.0.1:{PORT2},http://hello:world3@127.0.0.1:{PORT3}')

20
tests/basic.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
set -ex
script_dir=$(dirname "$PWD/$0")
[ "$1" ] || exit 1
naive="$PWD/$1"
. ./get-sysroot.sh
if [ "$WITH_ANDROID_IMG" ]; then
rootfs="$PWD/out/sysroot-build/android/$WITH_ANDROID_IMG"
elif [ "$WITH_SYSROOT" ]; then
rootfs="$PWD/$WITH_SYSROOT"
fi
cd /tmp
python3 "$script_dir"/basic.py --naive="$naive" --rootfs="$rootfs" --target_cpu="$target_cpu" --server_protocol=https
python3 "$script_dir"/basic.py --naive="$naive" --rootfs="$rootfs" --target_cpu="$target_cpu" --server_protocol=http

95
tests/qemu-howto.md Normal file
View File

@ -0,0 +1,95 @@
# Debug ARM Cortex-A9 static in QEMU
```
export EXTRA_FLAGS='target_cpu="arm" target_os="openwrt" arm_version=0 arm_cpu="cortex-a9" arm_float_abi="soft" arm_use_neon=false build_static=true no_madvise_syscall=true'
export OPENWRT_FLAGS='arch=arm_cortex-a9-static release=23.05.0 gcc_ver=12.3.0 target=bcm53xx subtarget=generic'
./get-clang.sh
./build.sh
```
See https://wiki.qemu.org/Documentation/Networking for example.
```
$ wget https://downloads.openwrt.org/releases/23.05.2/targets/armsr/armv7/openwrt-23.05.2-armsr-armv7-generic-initramfs-kernel.bin
$ qemu-system-arm -nographic -M virt -m 1024 -kernel openwrt-23.05.2-armsr-armv7-generic-initramfs-kernel.bin -device virtio-net,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:1080
...
root@OpenWrt:/# ip link del br-lan
root@OpenWrt:/# ip addr add 10.0.2.15/24 dev eth0
root@OpenWrt:/# ip route add default via 10.0.2.2
root@OpenWrt:/# nft flush ruleset
root@OpenWrt:/# echo nameserver 10.0.2.3 >/etc/resolv.conf
root@OpenWrt:/# scp user@10.0.2.2:/tmp/naive .
root@OpenWrt:/# ./naive --listen=socks://0.0.0.0:1080 --proxy=https://user:pass@example.com --log
user@host:/tmp$ curl -v --proxy socks5h://127.0.0.1:5555 example.com
```
Install GDB
```
root@OpenWrt:/# sed -i -e "s/https/http/" /etc/opkg/distfeeds.conf
root@OpenWrt:/# echo option http_proxy http://10.0.2.2:8080/ >>/etc/opkg.conf
root@OpenWrt:/# opkg update
root@OpenWrt:/# opkg install gdb
```
# Debug ARM64 static in QEMU
```
export EXTRA_FLAGS='target_cpu="arm64" target_os="openwrt" arm_cpu="cortex-a53" build_static=true no_madvise_syscall=true'
export OPENWRT_FLAGS='arch=aarch64_cortex-a53-static release=23.05.0 gcc_ver=12.3.0 target=sunxi subtarget=cortexa53'
./get-clang.sh
./build.sh
```
```
$ wget https://downloads.openwrt.org/releases/23.05.2/targets/armsr/armv8/openwrt-23.05.2-armsr-armv8-generic-initramfs-kernel.bin
$ qemu-system-aarch64 -m 1024 -M virt -cpu cortex-a53 -nographic -kernel openwrt-23.05.2-armsr-armv8-generic-initramfs-kernel.bin -device virtio-net,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:1080
...
root@OpenWrt:/# ip link del br-lan
root@OpenWrt:/# ip addr add 10.0.2.15/24 dev eth0
root@OpenWrt:/# ip route add default via 10.0.2.2
root@OpenWrt:/# nft flush ruleset
root@OpenWrt:/# echo nameserver 10.0.2.3 >/etc/resolv.conf
root@OpenWrt:/# scp user@10.0.2.2:/tmp/naive .
root@OpenWrt:/# ./naive --listen=socks://0.0.0.0:1080 --proxy=https://user:pass@example.com --log
user@host:/tmp$ curl -v --proxy socks5h://127.0.0.1:5555 example.com
```
# Debug MIPSEL static in QEMU
```
export EXTRA_FLAGS='target_cpu="mipsel" target_os="openwrt" mips_arch_variant="r2" mips_float_abi="soft" build_static=true no_madvise_syscall=true'
export OPENWRT_FLAGS='arch=mipsel_24kc-static release=23.05.0 gcc_ver=12.3.0 target=ramips subtarget=rt305x'
./get-clang.sh
./build.sh
```
```
$ wget https://downloads.openwrt.org/snapshots/targets/malta/le/lede-malta-le-vmlinux-initramfs.elf
$ qemu-system-mipsel -nographic -M malta -kernel lede-malta-le-vmlinux-initramfs.elf -m 64 -device pcnet,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:1080
...
(eth0 is set up by DHCP)
root@LEDE:/# iptables -F
(scp is too old)
user@host:/tmp$ nc -l -p 2222 <./naive
root@LEDE:/# nc 10.0.2.2 2222 >naive
^C
root@LEDE:/# chmod +x ./naive
user@host:/tmp$ nc -l -p2222 </etc/ssl/certs/ca-certificates.crt
root@LEDE:/# mkdir -p /etc/ssl/certs/
root@LEDE:/# nc 10.0.2.2 2222 >/etc/ssl/certs/ca-certificates.crt
^C
root@LEDE:/# ./naive --listen=socks://0.0.0.0:1080 --proxy=https://user:pass@example.com --log
user@host:/tmp$ curl -v --proxy socks5h://127.0.0.1:5555 example.com
```
## To exit QEMU in -nographic:
Press Ctrl-A
Press X