Skip to content

Commit 8c26ff5

Browse files
committed
stealth(novpn): direct-connection denylist + no-VPN local proxy mode
1 parent ce3da4c commit 8c26ff5

32 files changed

Lines changed: 2476 additions & 135 deletions
Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
1-
if(NOT ${ANDROID_STL} MATCHES "_shared")
1+
if(NOT "${ANDROID_STL}" MATCHES "_shared")
22
return()
33
endif()
44

5-
function(configure_shared_stl lib_path so_base)
5+
set(NDK_PREBUILT_DIR "${ANDROID_NDK}/toolchains/llvm/prebuilt")
6+
7+
if(DEFINED ANDROID_HOST_TAG AND EXISTS "${NDK_PREBUILT_DIR}/${ANDROID_HOST_TAG}")
8+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/${ANDROID_HOST_TAG}")
9+
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND EXISTS "${NDK_PREBUILT_DIR}/linux-x86_64")
10+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/linux-x86_64")
11+
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
12+
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$" AND EXISTS "${NDK_PREBUILT_DIR}/darwin-arm64")
13+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/darwin-arm64")
14+
elseif(EXISTS "${NDK_PREBUILT_DIR}/darwin-x86_64")
15+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/darwin-x86_64")
16+
elseif(EXISTS "${NDK_PREBUILT_DIR}/darwin-arm64")
17+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/darwin-arm64")
18+
endif()
19+
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows" AND EXISTS "${NDK_PREBUILT_DIR}/windows-x86_64")
20+
set(NDK_PREBUILT_ROOT "${NDK_PREBUILT_DIR}/windows-x86_64")
21+
endif()
22+
23+
if(NOT NDK_PREBUILT_ROOT)
24+
message(FATAL_ERROR "No Android NDK prebuilt toolchain found for ${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR} under ${NDK_PREBUILT_DIR}; set ANDROID_HOST_TAG if needed")
25+
endif()
26+
27+
if(NOT EXISTS "${NDK_PREBUILT_ROOT}")
28+
message(FATAL_ERROR "Android NDK prebuilt toolchain does not exist: ${NDK_PREBUILT_ROOT}")
29+
endif()
30+
31+
function(configure_shared_stl so_base)
632
message("Configuring STL ${so_base} for ${ANDROID_ABI}")
733

8-
if(${ANDROID_ABI} STREQUAL "arm64-v8a")
9-
set(LIBCXX_PATH "${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android/lib${so_base}.so")
10-
elseif(${ANDROID_ABI} STREQUAL "armeabi-v7a")
11-
set(LIBCXX_PATH "${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/arm-linux-androideabi/lib${so_base}.so")
12-
elseif(${ANDROID_ABI} STREQUAL "x86")
13-
set(LIBCXX_PATH "${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/i686-linux-android/lib${so_base}.so")
14-
elseif(${ANDROID_ABI} STREQUAL "x86_64")
15-
set(LIBCXX_PATH "${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/x86_64-linux-android/lib${so_base}.so")
34+
if("${ANDROID_ABI}" STREQUAL "arm64-v8a")
35+
set(LIBCXX_PATH "${NDK_PREBUILT_ROOT}/sysroot/usr/lib/aarch64-linux-android/lib${so_base}.so")
36+
elseif("${ANDROID_ABI}" STREQUAL "armeabi-v7a")
37+
set(LIBCXX_PATH "${NDK_PREBUILT_ROOT}/sysroot/usr/lib/arm-linux-androideabi/lib${so_base}.so")
38+
elseif("${ANDROID_ABI}" STREQUAL "x86")
39+
set(LIBCXX_PATH "${NDK_PREBUILT_ROOT}/sysroot/usr/lib/i686-linux-android/lib${so_base}.so")
40+
elseif("${ANDROID_ABI}" STREQUAL "x86_64")
41+
set(LIBCXX_PATH "${NDK_PREBUILT_ROOT}/sysroot/usr/lib/x86_64-linux-android/lib${so_base}.so")
1642
else()
1743
message(FATAL_ERROR "Unsupported ABI: ${ANDROID_ABI}")
1844
endif()
@@ -33,13 +59,13 @@ elseif("${ANDROID_STL}" STREQUAL "gabi++_shared")
3359
message(FATAL_ERROR "gabi++_shared was not configured by ndk-stl package")
3460
elseif("${ANDROID_STL}" STREQUAL "stlport_shared")
3561
# The STLport runtime (shared).
36-
configure_shared_stl("stlport" "stlport_shared")
62+
configure_shared_stl("stlport_shared")
3763
elseif("${ANDROID_STL}" STREQUAL "gnustl_shared")
3864
# The GNU STL (shared).
39-
configure_shared_stl("gnu-libstdc++/4.9" "gnustl_shared")
65+
configure_shared_stl("gnustl_shared")
4066
elseif("${ANDROID_STL}" STREQUAL "c++_shared")
41-
# The LLVM libc++ runtime (static).
42-
configure_shared_stl("llvm-libc++" "c++_shared")
67+
# The LLVM libc++ runtime (shared).
68+
configure_shared_stl("c++_shared")
4369
else()
4470
message(FATAL_ERROR "STL configuration ANDROID_STL=${ANDROID_STL} is not supported")
45-
endif()
71+
endif()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-checkdiscard class org.getlantern.lantern.service.LanternVpnService
2+
-checkdiscard class org.getlantern.lantern.service.QuickTileService

android/app/src/main/kotlin/org/getlantern/lantern/MainActivity.kt

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.getlantern.lantern
22

33
import android.Manifest
4+
import android.app.Service
45
import android.content.Intent
56
import android.content.pm.PackageManager
67
import android.net.VpnService
@@ -20,6 +21,7 @@ import org.getlantern.lantern.constant.VPNStatus
2021
import org.getlantern.lantern.handler.EventHandler
2122
import org.getlantern.lantern.handler.MethodHandler
2223
import org.getlantern.lantern.service.LanternVpnService
24+
import org.getlantern.lantern.service.NoVpnLanternService
2325
import org.getlantern.lantern.service.QuickTileService
2426
import org.getlantern.lantern.utils.AppLogger
2527
import org.getlantern.lantern.utils.VpnStatusManager
@@ -29,7 +31,7 @@ import org.getlantern.lantern.utils.logDir
2931
import org.getlantern.lantern.utils.setupDirs
3032

3133

32-
class MainActivity : FlutterFragmentActivity() {
34+
open class MainActivity : FlutterFragmentActivity() {
3335
companion object {
3436
const val TAG = "A/MainActivity"
3537
lateinit var instance: MainActivity
@@ -51,6 +53,16 @@ class MainActivity : FlutterFragmentActivity() {
5153

5254
private val serviceStartHandler = Handler(Looper.getMainLooper())
5355

56+
// NOTE: the stealth app uses its own MainActivity under the foundation.bridge
57+
// source set; this main-source-set activity only ever runs in the regular
58+
// (non-stealth) build where STEALTH_ENABLED is always false, so it references
59+
// the regular Lantern service classes directly.
60+
private val vpnServiceClass: Class<out Service>
61+
get() = LanternVpnService::class.java
62+
63+
private val noVpnServiceClass: Class<out Service>
64+
get() = NoVpnLanternService::class.java
65+
5466

5567
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
5668
super.configureFlutterEngine(flutterEngine)
@@ -94,18 +106,25 @@ class MainActivity : FlutterFragmentActivity() {
94106
if (pendingServiceStart && retryCountResume < maxRetriesResume) {
95107
retryCountResume++
96108
AppLogger.d(TAG, "Retrying pending service start")
97-
startLanternService()
109+
retryServiceStart()
98110
}
99111
}
100112

101113
private fun startLanternService() {
102114
AppLogger.d(TAG, "Starting LanternService")
103-
if (isServiceRunning(this, LanternVpnService::class.java)) {
115+
if (BuildConfig.STEALTH_NO_VPN) {
116+
AppLogger.d(TAG, "Stealth no-VPN build skips proxy autostart")
117+
pendingServiceStart = false
118+
retryCount = 0
119+
retryCountResume = 0
120+
return
121+
}
122+
if (isServiceRunning(this, vpnServiceClass)) {
104123
AppLogger.d(TAG, "LanternService is already running")
105124
return
106125
}
107126
try {
108-
val radianceIntent = Intent(this, LanternVpnService::class.java).apply {
127+
val radianceIntent = Intent(this, vpnServiceClass).apply {
109128
action = LanternVpnService.ACTION_START_RADIANCE
110129
}
111130
startService(radianceIntent)
@@ -125,6 +144,27 @@ class MainActivity : FlutterFragmentActivity() {
125144
}
126145
}
127146

147+
private fun startNoVpnProxyService() {
148+
if (isServiceRunning(this, noVpnServiceClass)) {
149+
AppLogger.d(TAG, "NoVpnLanternService is already running; sending start action")
150+
}
151+
try {
152+
ContextCompat.startForegroundService(this, Intent(this, noVpnServiceClass).apply {
153+
action = NoVpnLanternService.ACTION_START_PROXY
154+
})
155+
AppLogger.d(TAG, "NoVpnLanternService started")
156+
pendingServiceStart = false
157+
retryCount = 0
158+
retryCountResume = 0
159+
} catch (e: IllegalStateException) {
160+
AppLogger.e(TAG, "Cannot start no-VPN proxy service in background: ${e.message}")
161+
pendingServiceStart = true
162+
} catch (e: Exception) {
163+
AppLogger.e(TAG, "Error starting no-VPN proxy service", e)
164+
handleImmediateRetry()
165+
}
166+
}
167+
128168
private fun handleImmediateRetry() {
129169
AppLogger.d(TAG, "Handling immediate retry for LanternService start")
130170
if (retryCount < maxRetries) {
@@ -133,7 +173,7 @@ class MainActivity : FlutterFragmentActivity() {
133173

134174
AppLogger.d(TAG, "Scheduling immediate retry #$retryCount in ${delay}ms")
135175
serviceStartHandler.postDelayed({
136-
startLanternService()
176+
retryServiceStart()
137177
}, delay)
138178
} else {
139179
/*
@@ -148,8 +188,20 @@ class MainActivity : FlutterFragmentActivity() {
148188
}
149189
}
150190

191+
private fun retryServiceStart() {
192+
if (BuildConfig.STEALTH_NO_VPN) {
193+
startNoVpnProxyService()
194+
} else {
195+
startLanternService()
196+
}
197+
}
198+
151199

152200
fun startVPN() {
201+
if (BuildConfig.STEALTH_NO_VPN) {
202+
startNoVpnProxyService()
203+
return
204+
}
153205
if (!isVPNServiceReady()) {
154206
AppLogger.d(TAG, "VPN service not ready")
155207
return
@@ -167,7 +219,7 @@ class MainActivity : FlutterFragmentActivity() {
167219
}
168220

169221
try {
170-
val vpnIntent = Intent(this, LanternVpnService::class.java).apply {
222+
val vpnIntent = Intent(this, vpnServiceClass).apply {
171223
action = LanternVpnService.ACTION_START_VPN
172224
}
173225
ContextCompat.startForegroundService(this, vpnIntent)
@@ -180,6 +232,13 @@ class MainActivity : FlutterFragmentActivity() {
180232
}
181233

182234
fun connectToServer(tag: String) {
235+
if (BuildConfig.STEALTH_NO_VPN) {
236+
ContextCompat.startForegroundService(this, Intent(this, noVpnServiceClass).apply {
237+
action = NoVpnLanternService.ACTION_CONNECT_TO_SERVER
238+
putExtra("tag", tag)
239+
})
240+
return
241+
}
183242
if (!isVPNServiceReady()) {
184243
AppLogger.d(TAG, "VPN service not ready")
185244
return
@@ -196,7 +255,7 @@ class MainActivity : FlutterFragmentActivity() {
196255
}
197256

198257
try {
199-
val vpnIntent = Intent(this, LanternVpnService::class.java).apply {
258+
val vpnIntent = Intent(this, vpnServiceClass).apply {
200259
action = LanternVpnService.ACTION_CONNECT_TO_SERVER
201260
putExtra("tag", tag)
202261
}
@@ -211,7 +270,20 @@ class MainActivity : FlutterFragmentActivity() {
211270

212271

213272
fun stopVPN() {
214-
if (isServiceRunning(this, LanternVpnService::class.java)) {
273+
if (BuildConfig.STEALTH_NO_VPN) {
274+
if (isServiceRunning(this, noVpnServiceClass)) {
275+
startService(Intent(this, noVpnServiceClass).apply {
276+
action = NoVpnLanternService.ACTION_STOP_PROXY
277+
})
278+
} else {
279+
CoroutineScope(Dispatchers.Main).launch {
280+
runCatching { Mobile.stopVPN() }
281+
VpnStatusManager.postVPNStatus(VPNStatus.Disconnected)
282+
}
283+
}
284+
return
285+
}
286+
if (isServiceRunning(this, vpnServiceClass)) {
215287
LanternApp.application.sendBroadcast(
216288
Intent(LanternVpnService.ACTION_STOP_VPN)
217289
.setPackage(LanternApp.application.packageName)

android/app/src/main/kotlin/org/getlantern/lantern/handler/EventHandler.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import kotlinx.coroutines.SupervisorJob
1111
import kotlinx.coroutines.launch
1212
import lantern.io.mobile.LogSubscription
1313
import lantern.io.mobile.Mobile
14-
import lantern.io.utils.FlutterEvent
1514
import lantern.io.utils.LogListener
1615
import org.getlantern.lantern.apps.AppDataHandler
1716
import org.getlantern.lantern.constant.VPNStatus
17+
import org.getlantern.lantern.utils.AppEvent
1818
import org.getlantern.lantern.utils.AppLogger
1919
import org.getlantern.lantern.utils.Event
2020
import org.getlantern.lantern.utils.FlutterEventStream
@@ -41,7 +41,7 @@ class EventHandler : FlutterPlugin {
4141
private var appDataHandler: AppDataHandler? = null
4242

4343
private var statusObserver: Observer<Event<VPNStatus>>? = null
44-
private var flutterEventObserver: Observer<Event<FlutterEvent>>? = null
44+
private var flutterEventObserver: Observer<Event<AppEvent>>? = null
4545
var job: Job? = null
4646
private var logsSubscription: LogSubscription? = null
4747
private var logsListener: LogListener? = null

0 commit comments

Comments
 (0)