* feat: add a root, system-wide run mode without VpnService Adds an optional Root mode for rooted devices that routes the whole device's traffic through the existing in-process core without Android's VpnService, plus an opt-in LAN/tethering sharing feature. Non-root devices are unaffected and keep VPN / Proxy-only (VPN stays the default). - ERunMode (VPN, PROXY_ONLY, TUN2SOCKS) persisted in the existing PREF_MODE; RootManager gates root modes (greyed-out for non-root, service refuses to start). - CoreRootService + core/root/RootProxyManager run hev-socks5-tunnel as a standalone root process into the core's SOCKS inbound, steered by an iptables mangle MARK chain + a dedicated route table. Full TCP + UDP. hev-socks5-tunnel is the same engine already bundled for the VPN hev path, so no new third-party dependency is added. - Capture parity with VpnService incl. per-app proxy/bypass; DNS funneled into the core (netd-aware, no uid filter) so names resolve through the configured resolver with no LAN-resolver leak. - IPv6 parity: routed into the tun when enabled, otherwise native v6 is blackholed for the captured apps (REJECT) so they fall back to v4-through-proxy, like a v4-only VpnService. - MTU taken from the existing VPN MTU setting; hev tun multi-queue + SOCKS tcp-fastopen enabled. - CI fetches the hev-socks5-tunnel binary per-ABI from heiher/hev-socks5-tunnel releases (the same upstream the VPN hev path uses). * build: compile libhevsockstun.so from source instead of downloading Build the standalone hev-socks5-tunnel binary used by Root mode from the pinned hev-socks5-tunnel submodule in compile-hevtun.sh, alongside the existing JNI shared library, and drop the prebuilt release download from the build workflow. Both hev artifacts now come from the same in-tree source, so the binary is fully auditable and version-locked to the submodule rather than a fetched release asset. The executable is built without -DENABLE_LIBRARY (so hev-main.c's main() is included) via BUILD_EXECUTABLE, reusing the NDK toolchain already used for the JNI library. * refactor(root): address review feedback - LAN-sharing guard now checks the cheap, usually-false PREF_ROOT_LAN_SHARING preference before RootManager.cachedRoot(), so the common path short-circuits without touching root state. - Move RootManager into the core.root package next to RootProxyManager and RootShell (CoreRootService stays under service/). - Probe su only when the user opts into a root feature — selecting a root mode or enabling LAN sharing — instead of automatically on every Settings open. If root is denied the selection is reverted with a toast. This avoids an unsolicited root-grant prompt for the common non-root case; root mode for a persisted selection is still re-verified when the service starts. * refactor(root): use coroutines instead of Thread for su probing Replace raw Thread usage in the root path with kotlinx coroutines, as requested in review. RootManager.refreshAsync (a callback + daemon Thread) becomes a suspending refresh() that runs the blocking su probe on Dispatchers.IO and returns the result. Callers updated accordingly: - SettingsActivity probes on demand via lifecycleScope.launch and updates the UI directly on resume (no manual runOnUiThread). - CoreVpnService starts the LAN-sharing client over CoroutineScope(IO) instead of a daemon Thread. * fix(root): don't capture all apps when per-app proxy resolves no uids In allow (proxy-only) mode the mangle/v6 builders fell through to the catch-all "mark/reject everything" branch whenever selectedUids was empty. That is a fail-open: if the selected packages momentarily fail to resolve to uids (e.g. at early boot, before PackageManager is ready), every unselected app gets tunneled instead of none — a privacy leak and the cause of per-app "proxying everything" after a reboot. Gate the catch-all on the mode itself (all-apps or bypass) rather than on "selected list happened to be non-empty". In allow mode mark only the resolved uids; if none resolved, mark nothing (fail closed). Mirror the same fix in the IPv6 blackhole chain. * fix(root): wait for async rule setup before teardown on stop CoreRootService/CoreVpnService post the foreground notification as soon as the core starts but install the root routing rules in a launched coroutine, which can take seconds (the setup script waits for the tun device to appear). If the user stops the service during that window, onDestroy/stopAllService ran the synchronous teardown first and the still-running setup then re-installed the rules and tun afterwards — leaving orphan routing rules and a tun forwarding into a now-dead core, which blackholes all traffic until the next start/stop cycle clears it (the "disconnect from the notification kills the internet, reconnect+disconnect to fix it" bug). Track the setup job and cancelAndJoin it before tearing down so teardown always runs last and removes everything the setup installed. * Adjust root package and add RootLanSharing object * Remove ERunMode , add PREF_ROOT_MODE_ENABLE * fix(root): handle tethered clients' IPv6 in LAN sharing to stop leaks LAN/tethering sharing only set up IPv4 forwarding for clients, so a hotspot/USB-tethered client with a native (RA-assigned) global IPv6 egressed the upstream interface directly, bypassing the proxy — an IPv6 leak. buildLanShareSetup now handles forwarded clients' v6: - IPv6 enabled: route it through the tun. A mangle PREROUTING chain marks non-LOCAL-sourced (forwarded) v6 into the tun route table, keeps loopback/link-local/ULA/multicast direct, and hijacks client DNS; FORWARD accepts traffic to/from the tun. A trailing REJECT fails closed so anything not marked into the tun (e.g. addrtype match unavailable) is dropped instead of leaked. - IPv6 disabled: REJECT all forwarded v6 (the device's own v6 is already blackholed in OUTPUT). Teardown drops the two new ip6tables chains; the v6 route/rule into the tun table were already cleaned. Ported from vincentng295/Magic_V2Ray cae4f7f. * Update build.gradle.kts * Adjust settings --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
104 lines
3.3 KiB
Bash
104 lines
3.3 KiB
Bash
#!/bin/bash
|
|
set -o errexit
|
|
set -o pipefail
|
|
set -o nounset
|
|
# Set magic variables for current file & dir
|
|
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
|
|
__base="$(basename ${__file} .sh)"
|
|
if [[ ! -d $NDK_HOME ]]; then
|
|
echo "Android NDK: NDK_HOME not found. please set env \$NDK_HOME"
|
|
exit 1
|
|
fi
|
|
TMPDIR=$(mktemp -d)
|
|
clear_tmp () {
|
|
rm -rf $TMPDIR
|
|
}
|
|
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; clear_tmp; exit 1' ERR INT
|
|
|
|
ABIS="armeabi-v7a arm64-v8a x86 x86_64"
|
|
|
|
mkdir -p "$TMPDIR/jni"
|
|
pushd "$TMPDIR"
|
|
|
|
ln -s "$__dir/hev-socks5-tunnel" jni/hev-socks5-tunnel
|
|
|
|
# 1) JNI shared library (libhev-socks5-tunnel.so) — loaded in-process by
|
|
# com.v2ray.ang.service.TProxyService for the VpnService hev tun mode.
|
|
echo 'include $(call all-subdir-makefiles)' > jni/Android.mk
|
|
|
|
"$NDK_HOME/ndk-build" \
|
|
NDK_PROJECT_PATH=. \
|
|
APP_BUILD_SCRIPT=jni/Android.mk \
|
|
"APP_ABI=$ABIS" \
|
|
APP_PLATFORM=android-24 \
|
|
NDK_LIBS_OUT="$TMPDIR/libs" \
|
|
NDK_OUT="$TMPDIR/obj" \
|
|
"APP_CFLAGS=-O3 -DPKGNAME=com/v2ray/ang/service" \
|
|
"APP_LDFLAGS=-Wl,--build-id=none -Wl,--hash-style=gnu" \
|
|
|
|
# 2) Standalone executable (libhevsockstun.so) — run as a separate root
|
|
# process by com.v2ray.ang.core.root for the Root run mode. Same hev source,
|
|
# no -DENABLE_LIBRARY so hev-main.c's main() is built, and BUILD_EXECUTABLE
|
|
# instead of a shared library. It creates its own tun and reads a YAML config.
|
|
cat > jni/exec.mk <<'EXECMK'
|
|
TOP_PATH := $(call my-dir)/hev-socks5-tunnel
|
|
|
|
ifeq ($(filter $(modules-get-list),yaml),)
|
|
include $(TOP_PATH)/third-part/yaml/Android.mk
|
|
endif
|
|
ifeq ($(filter $(modules-get-list),lwip),)
|
|
include $(TOP_PATH)/third-part/lwip/Android.mk
|
|
endif
|
|
ifeq ($(filter $(modules-get-list),hev-task-system),)
|
|
include $(TOP_PATH)/third-part/hev-task-system/Android.mk
|
|
endif
|
|
|
|
LOCAL_PATH := $(TOP_PATH)
|
|
SRCDIR := $(LOCAL_PATH)/src
|
|
|
|
include $(CLEAR_VARS)
|
|
include $(LOCAL_PATH)/build.mk
|
|
LOCAL_MODULE := hevsockstun
|
|
LOCAL_SRC_FILES := $(patsubst $(SRCDIR)/%,src/%,$(SRCFILES))
|
|
LOCAL_C_INCLUDES := \
|
|
$(LOCAL_PATH)/src \
|
|
$(LOCAL_PATH)/src/misc \
|
|
$(LOCAL_PATH)/src/core/include \
|
|
$(LOCAL_PATH)/third-part/yaml/include \
|
|
$(LOCAL_PATH)/third-part/lwip/src/include \
|
|
$(LOCAL_PATH)/third-part/lwip/src/ports/include \
|
|
$(LOCAL_PATH)/third-part/hev-task-system/include
|
|
LOCAL_CFLAGS += -DFD_SET_DEFINED -DSOCKLEN_T_DEFINED
|
|
LOCAL_CFLAGS += $(VERSION_CFLAGS)
|
|
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
|
LOCAL_CFLAGS += -mfpu=neon
|
|
endif
|
|
LOCAL_STATIC_LIBRARIES := yaml lwip hev-task-system
|
|
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384
|
|
LOCAL_LDFLAGS += -Wl,-z,common-page-size=16384
|
|
include $(BUILD_EXECUTABLE)
|
|
EXECMK
|
|
|
|
"$NDK_HOME/ndk-build" \
|
|
NDK_PROJECT_PATH=. \
|
|
APP_BUILD_SCRIPT=jni/exec.mk \
|
|
"APP_ABI=$ABIS" \
|
|
APP_PLATFORM=android-24 \
|
|
NDK_LIBS_OUT="$TMPDIR/libs-exec" \
|
|
NDK_OUT="$TMPDIR/obj-exec" \
|
|
"APP_CFLAGS=-O3" \
|
|
"APP_LDFLAGS=-Wl,--build-id=none -Wl,--hash-style=gnu" \
|
|
|
|
# Stage both artifacts under libs/<abi>/. The executable is renamed to
|
|
# lib*.so so the APK installer extracts it into nativeLibraryDir as an
|
|
# executable file (filename distinct from the JNI library above).
|
|
mkdir -p "$__dir/libs"
|
|
cp -r "$TMPDIR/libs/"* "$__dir/libs/"
|
|
for abi in $ABIS; do
|
|
cp "$TMPDIR/libs-exec/$abi/hevsockstun" "$__dir/libs/$abi/libhevsockstun.so"
|
|
done
|
|
|
|
popd
|
|
rm -rf $TMPDIR
|