From aaa02847ed9f6d4264b567317a554ca0b8defe3f Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 14 Mar 2024 20:51:12 +0530 Subject: [PATCH 01/81] improved dns handling while adding to okhttp client --- .../LocalBlocklistCoordinator.kt | 34 ++++++--- .../RemoteBlocklistCoordinator.kt | 74 +++++++++---------- .../customdownloader/RetrofitManager.kt | 42 ++++++----- .../bravedns/download/AppDownloadManager.kt | 19 ++++- .../download/BlocklistDownloadHelper.kt | 45 ++++++----- .../bravedns/download/DownloadWatcher.kt | 2 +- .../bravedns/scheduler/PaymentWorker.kt | 21 ++++-- .../bravedns/service/TcpProxyHelper.kt | 25 +++++-- 8 files changed, 164 insertions(+), 98 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt index fea819af5..5102e7c95 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt @@ -45,9 +45,6 @@ import com.celzero.bravedns.util.Utilities.blocklistDownloadBasePath import com.celzero.bravedns.util.Utilities.calculateMd5 import com.celzero.bravedns.util.Utilities.getTagValueFromJson import com.celzero.bravedns.util.Utilities.tempDownloadBasePath -import okhttp3.ResponseBody -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream @@ -56,6 +53,9 @@ import java.io.InputStream import java.io.OutputStream import java.util.concurrent.CancellationException import java.util.concurrent.TimeUnit +import okhttp3.ResponseBody +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject class LocalBlocklistCoordinator(val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { @@ -82,6 +82,7 @@ class LocalBlocklistCoordinator(val context: Context, workerParams: WorkerParame } override suspend fun doWork(): Result { + if (DEBUG) Log.d(LOG_TAG_DOWNLOAD, "Local blocklist download started") try { val startTime = inputData.getLong("workerStartTime", 0) val timestamp = inputData.getLong("blocklistTimestamp", 0) @@ -216,23 +217,38 @@ class LocalBlocklistCoordinator(val context: Context, workerParams: WorkerParame private suspend fun startFileDownload( context: Context, url: String, - fileName: String + fileName: String, + retryCount: Int = 0 ): Boolean { // enable the OkHttp's logging only in debug mode for testing if (DEBUG) OkHttpDebugLogging.enableHttp2() if (DEBUG) OkHttpDebugLogging.enableTaskRunner() - // create okhttp client with base url - val retrofit = getBlocklistBaseBuilder().build().create(IBlocklistDownload::class.java) - val response = retrofit.downloadLocalBlocklistFile(url, persistentState.appVersion, "") + try { + // create okhttp client with base url + val retrofit = + getBlocklistBaseBuilder(retryCount).build().create(IBlocklistDownload::class.java) + val response = retrofit.downloadLocalBlocklistFile(url, persistentState.appVersion, "") - return if (response?.isSuccessful == true) { - downloadFile(context, response.body(), fileName) + if (response?.isSuccessful == true) { + return downloadFile(context, response.body(), fileName) + } + } catch (e: Exception) { + Log.e(LOG_TAG_DOWNLOAD, "Error in startFileDownload: ${e.message}", e) + } + return if (isRetryRequired(retryCount)) { + Log.i(LOG_TAG_DOWNLOAD, "retrying download($url) $fileName, count: $retryCount") + startFileDownload(context, url, fileName, retryCount + 1) } else { + Log.i(LOG_TAG_DOWNLOAD, "download failed for $fileName, retry: $retryCount") false } } + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 + } + private fun downloadFile(context: Context, body: ResponseBody?, fileName: String): Boolean { if (body == null) { return false diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt index c8026b435..1827fea24 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt @@ -24,17 +24,17 @@ import com.celzero.bravedns.download.BlocklistDownloadHelper import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.service.RethinkBlocklistManager import com.celzero.bravedns.util.Constants -import com.celzero.bravedns.util.Logger +import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD import com.celzero.bravedns.util.RemoteFileTagUtil import com.celzero.bravedns.util.Utilities import com.google.gson.JsonObject -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import retrofit2.converter.gson.GsonConverterFactory import java.io.File import java.io.IOException import java.util.concurrent.CancellationException import java.util.concurrent.TimeUnit +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import retrofit2.converter.gson.GsonConverterFactory class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { @@ -47,6 +47,7 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam } override suspend fun doWork(): Result { + Log.i(LOG_TAG_DOWNLOAD, "Remote blocklist download worker started") try { val startTime = inputData.getLong("workerStartTime", 0) val timestamp = inputData.getLong("blocklistTimestamp", 0) @@ -80,8 +81,7 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam } } } catch (ex: CancellationException) { - Log.e( - Logger.LOG_TAG_DOWNLOAD, + Log.e(LOG_TAG_DOWNLOAD, "Local blocklist download, received cancellation exception: ${ex.message}", ex ) @@ -89,38 +89,42 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam return Result.failure() } - private suspend fun downloadRemoteBlocklist(timestamp: Long): Boolean { - Log.i(Logger.LOG_TAG_DOWNLOAD, "Download remote blocklist: $timestamp") - - val retrofit = - RetrofitManager.getBlocklistBaseBuilder() - .addConverterFactory(GsonConverterFactory.create()) - .build() - val retrofitInterface = retrofit.create(IBlocklistDownload::class.java) - val response = - retrofitInterface.downloadRemoteBlocklistFile( - Constants.FILETAG_TEMP_DOWNLOAD_URL, - persistentState.appVersion, - "" - ) + private suspend fun downloadRemoteBlocklist(timestamp: Long, retryCount: Int = 0): Boolean { + Log.i(LOG_TAG_DOWNLOAD, "Download remote blocklist: $timestamp") + try { + val retrofit = + RetrofitManager.getBlocklistBaseBuilder(retryCount) + .addConverterFactory(GsonConverterFactory.create()) + .build() + val retrofitInterface = retrofit.create(IBlocklistDownload::class.java) + val response = + retrofitInterface.downloadRemoteBlocklistFile( + Constants.FILETAG_TEMP_DOWNLOAD_URL, + persistentState.appVersion, + "" + ) - Log.i( - Logger.LOG_TAG_DOWNLOAD, - "Response received on remote blocklist request: ${response?.isSuccessful}" - ) + Log.i(LOG_TAG_DOWNLOAD, "response rcvd for remote blocklist, res: ${response?.isSuccessful}") - return if (response?.isSuccessful == true) { - val isDownloadSuccess = saveRemoteFile(response.body(), timestamp) - isDownloadSuccess + if (response?.isSuccessful == true) { + return saveRemoteFile(response.body(), timestamp) + } + } catch (ex: Exception) { + Log.e(LOG_TAG_DOWNLOAD, "err in downloadRemoteBlocklist: ${ex.message}", ex) + } + return if (isRetryRequired(retryCount)) { + Log.i(LOG_TAG_DOWNLOAD, "retrying the downloadRemoteBlocklist") + downloadRemoteBlocklist(timestamp, retryCount + 1) } else { - Log.i( - Logger.LOG_TAG_DOWNLOAD, - "Remote blocklist download failure, call? ${response?.body()}, response: $response " - ) + Log.i(LOG_TAG_DOWNLOAD, "retry count exceeded, returning null") false } } + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 + } + private suspend fun saveRemoteFile(jsonObject: JsonObject?, timestamp: Long): Boolean { try { val filetag = makeFile(timestamp) ?: return false @@ -134,7 +138,7 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam timestamp ) } catch (e: IOException) { - Log.w(Logger.LOG_TAG_DOWNLOAD, "could not create filetag.json at version $timestamp", e) + Log.w(LOG_TAG_DOWNLOAD, "could not create filetag.json at version $timestamp", e) } return false } @@ -157,11 +161,7 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam } return filePath } catch (e: IOException) { - Log.e( - Logger.LOG_TAG_DOWNLOAD, - "Could not create remote blocklist folder/file: $timestamp" + e.message, - e - ) + Log.e(LOG_TAG_DOWNLOAD, "err creating remote blocklist, ts: $timestamp" + e.message, e) } return null } diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt index 7f08ac86f..96cbe4745 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt @@ -18,13 +18,13 @@ package com.celzero.bravedns.customdownloader import android.util.Log import com.celzero.bravedns.util.Constants import com.celzero.bravedns.util.Logger +import java.net.InetAddress +import java.util.concurrent.TimeUnit import okhttp3.Dns import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.dnsoverhttps.DnsOverHttps import retrofit2.Retrofit -import java.net.InetAddress -import java.util.concurrent.TimeUnit class RetrofitManager { @@ -37,35 +37,41 @@ class RetrofitManager { FALLBACK_DNS } - fun getBlocklistBaseBuilder(): Retrofit.Builder { - return Retrofit.Builder().baseUrl(Constants.DOWNLOAD_BASE_URL).client(okHttpClient()) + fun getBlocklistBaseBuilder(dnsId: Int): Retrofit.Builder { + return Retrofit.Builder() + .baseUrl(Constants.DOWNLOAD_BASE_URL) + .client(okHttpClient(dnsId)) } - fun getWarpBaseBuilder(): Retrofit.Builder { - return Retrofit.Builder().baseUrl(Constants.DOWNLOAD_BASE_URL).client(okHttpClient()) + fun getWarpBaseBuilder(dnsId: Int): Retrofit.Builder { + return Retrofit.Builder() + .baseUrl(Constants.DOWNLOAD_BASE_URL) + .client(okHttpClient(dnsId)) } - fun getTcpProxyBaseBuilder(): Retrofit.Builder { - return Retrofit.Builder().baseUrl(Constants.TCP_PROXY_BASE_URL).client(okHttpClient()) + fun getTcpProxyBaseBuilder(dnsId: Int): Retrofit.Builder { + return Retrofit.Builder() + .baseUrl(Constants.TCP_PROXY_BASE_URL) + .client(okHttpClient(dnsId)) } - fun okHttpClient(): OkHttpClient { + fun okHttpClient(dnsId: Int = 0): OkHttpClient { val b = OkHttpClient.Builder() b.connectTimeout(1, TimeUnit.MINUTES) b.readTimeout(20, TimeUnit.MINUTES) b.writeTimeout(5, TimeUnit.MINUTES) b.retryOnConnectionFailure(true) // If unset, the system-wide default DNS will be used. - customDns(b.build())?.let { b.dns(it) } + customDns(dnsId, b.build())?.let { b.dns(it) } return b.build() } // As of now, quad9 is used as default dns in okhttp client. - private fun customDns(bootstrapClient: OkHttpClient): Dns? { - enumValues().forEach { + private fun customDns(dnsId: Int, bootstrapClient: OkHttpClient): Dns? { + enumValues().forEach { _ -> try { - when (it) { - OkHttpDnsType.DEFAULT -> { + when (dnsId) { + OkHttpDnsType.DEFAULT.ordinal -> { return DnsOverHttps.Builder() .client(bootstrapClient) .url("https://dns.quad9.net/dns-query".toHttpUrl()) @@ -78,7 +84,7 @@ class RetrofitManager { .includeIPv6(true) .build() } - OkHttpDnsType.CLOUDFLARE -> { + OkHttpDnsType.CLOUDFLARE.ordinal -> { return DnsOverHttps.Builder() .client(bootstrapClient) .url("https://cloudflare-dns.com/dns-query".toHttpUrl()) @@ -91,7 +97,7 @@ class RetrofitManager { .includeIPv6(true) .build() } - OkHttpDnsType.GOOGLE -> { + OkHttpDnsType.GOOGLE.ordinal -> { return DnsOverHttps.Builder() .client(bootstrapClient) .url("https://dns.google/dns-query".toHttpUrl()) @@ -104,10 +110,10 @@ class RetrofitManager { .includeIPv6(true) .build() } - OkHttpDnsType.SYSTEM_DNS -> { + OkHttpDnsType.SYSTEM_DNS.ordinal -> { return Dns.SYSTEM } - OkHttpDnsType.FALLBACK_DNS -> { + OkHttpDnsType.FALLBACK_DNS.ordinal -> { // todo: return retrieved system dns return null } diff --git a/app/src/full/java/com/celzero/bravedns/download/AppDownloadManager.kt b/app/src/full/java/com/celzero/bravedns/download/AppDownloadManager.kt index 5980b27f8..100117cd7 100644 --- a/app/src/full/java/com/celzero/bravedns/download/AppDownloadManager.kt +++ b/app/src/full/java/com/celzero/bravedns/download/AppDownloadManager.kt @@ -181,15 +181,21 @@ class AppDownloadManager( // no need to proceed if the current and received timestamp is same if (updatableTs <= currentTs && !isRedownload) { + Log.i( + LOG_TAG_DNS, + "local blocklist update not required, current ts: $currentTs, updatable ts: $updatableTs" + ) return DownloadManagerStatus.NOT_REQUIRED } else { // no-op } if (persistentState.useCustomDownloadManager) { + Log.i(LOG_TAG_DNS, "initiating local blocklist download with custom download mgr") return initiateCustomDownloadManager(updatableTs) } + Log.i(LOG_TAG_DNS, "initiating local blocklist download with Android download mgr") return initiateAndroidDownloadManager(updatableTs) } @@ -198,9 +204,12 @@ class AppDownloadManager( if ( WorkScheduler.isWorkScheduled(context, DOWNLOAD_TAG) || WorkScheduler.isWorkScheduled(context, FILE_TAG) - ) + ) { + Log.i(LOG_TAG_DNS, "local blocklist download is already in progress, returning") return DownloadManagerStatus.FAILURE + } + Log.i(LOG_TAG_DNS, "local blocklist download is not in progress, starting the download") purge(context, timestamp, DownloadType.LOCAL) val downloadIds = LongArray(ONDEVICE_BLOCKLISTS_ADM.count()) ONDEVICE_BLOCKLISTS_ADM.forEachIndexed { i, it -> @@ -240,6 +249,10 @@ class AppDownloadManager( val updatableTs = getDownloadableTimestamp(response) if (updatableTs <= currentTs && !isRedownload) { + Log.i( + LOG_TAG_DNS, + "remote blocklist update not required, current ts: $currentTs, updatable ts: $updatableTs" + ) return false } else { // no-op @@ -258,8 +271,10 @@ class AppDownloadManager( context, RemoteBlocklistCoordinator.REMOTE_DOWNLOAD_WORKER ) - ) + ) { + Log.i(LOG_TAG_DNS, "remote blocklist download is already in progress, returning") return false + } startRemoteBlocklistCoordinator(timestamp) return true diff --git a/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt b/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt index 7c3a296ab..77e9a11a0 100644 --- a/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt +++ b/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt @@ -26,11 +26,11 @@ import com.celzero.bravedns.util.Constants.Companion.INIT_TIME_MS import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD import com.celzero.bravedns.util.Utilities.blocklistCanonicalPath import com.celzero.bravedns.util.Utilities.deleteRecursive +import java.io.File +import java.lang.RuntimeException import org.json.JSONException import org.json.JSONObject import retrofit2.converter.gson.GsonConverterFactory -import java.io.File -import java.io.IOException class BlocklistDownloadHelper { @@ -145,10 +145,14 @@ class BlocklistDownloadHelper { ): BlocklistUpdateServerResponse? { try { val retrofit = - RetrofitManager.getBlocklistBaseBuilder() + RetrofitManager.getBlocklistBaseBuilder(retryCount) .addConverterFactory(GsonConverterFactory.create()) .build() val retrofitInterface = retrofit.create(IBlocklistDownload::class.java) + Log.i( + LOG_TAG_DOWNLOAD, + "downloadAvailabilityCheck: ${Constants.ONDEVICE_BLOCKLIST_UPDATE_CHECK_QUERYPART_1}, ${Constants.ONDEVICE_BLOCKLIST_UPDATE_CHECK_QUERYPART_2}, $vcode, $timestamp" + ) val response = retrofitInterface.downloadAvailabilityCheck( Constants.ONDEVICE_BLOCKLIST_UPDATE_CHECK_QUERYPART_1, @@ -163,26 +167,26 @@ class BlocklistDownloadHelper { if (response?.isSuccessful == true) { val r = response.body()?.toString()?.let { JSONObject(it) } return processCheckDownloadResponse(r) - } else { - retryIfRequired(timestamp, vcode, retryCount) } - } catch (ignored: Exception) { - Log.w( - LOG_TAG_DOWNLOAD, - "exception in checkBlocklistUpdate: ${ignored.message}", - ignored - ) - retryIfRequired(timestamp, vcode, retryCount) + } catch (ex: Exception) { + Log.e(LOG_TAG_DOWNLOAD, "exception in checkBlocklistUpdate: ${ex.message}", ex) + Log.d(LOG_TAG_DOWNLOAD, "Ex: $ex", RuntimeException()) } - return null - } - - private suspend fun retryIfRequired(timestamp: Long, vcode: Int, retryCount: Int) { - if (retryCount > 3) { - return + Log.i( + LOG_TAG_DOWNLOAD, + "downloadAvailabilityCheck: failed, returning null, $retryCount" + ) + return if (isRetryRequired(retryCount)) { + Log.i(LOG_TAG_DOWNLOAD, "retrying the downloadAvailabilityCheck") + checkBlocklistUpdate(timestamp, vcode, retryCount + 1) + } else { + Log.i(LOG_TAG_DOWNLOAD, "retry count exceeded, returning null") + null } + } - checkBlocklistUpdate(timestamp, vcode, retryCount + 1) + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 } private fun processCheckDownloadResponse( @@ -207,8 +211,9 @@ class BlocklistDownloadHelper { return BlocklistUpdateServerResponse(version, shouldUpdate, timestamp) } catch (e: JSONException) { - throw IOException() + Log.e(LOG_TAG_DOWNLOAD, "Error in parsing the response: ${e.message}", e) } + return null } fun getDownloadableTimestamp(response: BlocklistUpdateServerResponse): Long { diff --git a/app/src/full/java/com/celzero/bravedns/download/DownloadWatcher.kt b/app/src/full/java/com/celzero/bravedns/download/DownloadWatcher.kt index cba180191..39a81ffee 100644 --- a/app/src/full/java/com/celzero/bravedns/download/DownloadWatcher.kt +++ b/app/src/full/java/com/celzero/bravedns/download/DownloadWatcher.kt @@ -50,7 +50,7 @@ class DownloadWatcher(val context: Context, workerParameters: WorkerParameters) private var downloadIds: MutableList? = mutableListOf() override fun doWork(): Result { - + Log.i(LOG_TAG_DOWNLOAD, "start download watcher, checking for download status") val startTime = inputData.getLong("workerStartTime", 0) downloadIds = inputData.getLongArray("downloadIds")?.toMutableList() if (DEBUG) Log.d(LOG_TAG_DOWNLOAD, "AppDownloadManager: $startTime, $downloadIds") diff --git a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt index 37572adac..7d5dc7a82 100644 --- a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt +++ b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt @@ -26,10 +26,10 @@ import com.celzero.bravedns.customdownloader.RetrofitManager import com.celzero.bravedns.service.TcpProxyHelper import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD +import java.util.concurrent.TimeUnit import org.json.JSONObject import org.koin.core.component.KoinComponent import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : CoroutineWorker(context, workerParameters), KoinComponent { @@ -70,11 +70,11 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : } } - private suspend fun getPaymentStatusFromServer(): TcpProxyHelper.PaymentStatus { + private suspend fun getPaymentStatusFromServer(retryCount: Int = 0): TcpProxyHelper.PaymentStatus { var paymentStatus = TcpProxyHelper.PaymentStatus.INITIATED try { val retrofit = - RetrofitManager.getTcpProxyBaseBuilder() + RetrofitManager.getTcpProxyBaseBuilder(retryCount) .addConverterFactory(GsonConverterFactory.create()) .build() val retrofitInterface = retrofit.create(ITcpProxy::class.java) @@ -92,7 +92,7 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : val status = jsonObject.optString(JSON_STATUS, "") val paymentStatusString = jsonObject.optString(JSON_PAYMENT_STATUS, "") paymentStatus = - TcpProxyHelper.PaymentStatus.values().find { it.name == paymentStatusString } + TcpProxyHelper.PaymentStatus.entries.find { it.name == paymentStatusString } ?: TcpProxyHelper.PaymentStatus.NOT_PAID Log.i( Logger.LOG_TAG_PROXY, @@ -101,6 +101,7 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : if (paymentStatus.isPaid() || paymentStatus.isFailed()) { TcpProxyHelper.updatePaymentStatus(paymentStatus) } + return paymentStatus } else { Log.w( Logger.LOG_TAG_PROXY, @@ -114,6 +115,16 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : e ) } - return paymentStatus + return if (isRetryRequired(retryCount) && paymentStatus == TcpProxyHelper.PaymentStatus.INITIATED) { + Log.i(LOG_TAG_DOWNLOAD, "retrying the downloadRemoteBlocklist") + getPaymentStatusFromServer(retryCount + 1) + } else { + Log.i(LOG_TAG_DOWNLOAD, "retry count exceeded, returning null") + return paymentStatus + } + } + + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 } } diff --git a/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt b/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt index 81c0228b2..57ea852a9 100644 --- a/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt +++ b/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt @@ -17,7 +17,11 @@ import com.celzero.bravedns.data.AppConfig import com.celzero.bravedns.database.TcpProxyEndpoint import com.celzero.bravedns.database.TcpProxyRepository import com.celzero.bravedns.scheduler.PaymentWorker +import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_PROXY +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -25,9 +29,6 @@ import org.json.JSONObject import org.koin.core.component.KoinComponent import org.koin.core.component.inject import retrofit2.converter.gson.GsonConverterFactory -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit object TcpProxyHelper : KoinComponent { @@ -127,11 +128,11 @@ object TcpProxyHelper : KoinComponent { return dateFormat.format(date) } - suspend fun publicKeyUsable(): Boolean { + suspend fun publicKeyUsable(retryCount: Int = 0): Boolean { var works = false try { val retrofit = - RetrofitManager.getTcpProxyBaseBuilder() + RetrofitManager.getTcpProxyBaseBuilder(retryCount) .addConverterFactory(GsonConverterFactory.create()) .build() val retrofitInterface = retrofit.create(ITcpProxy::class.java) @@ -152,6 +153,7 @@ object TcpProxyHelper : KoinComponent { LOG_TAG_PROXY, "tcp response for ${response.raw().request.url}, works? $works, minVersionCode: $minVersionCode, publicKey: $publicKey" ) + return works } else { Log.w(LOG_TAG_PROXY, "unsuccessful response for ${response?.raw()?.request?.url}") } @@ -162,7 +164,18 @@ object TcpProxyHelper : KoinComponent { e ) } - return works + + return if (isRetryRequired(retryCount) && !works) { + Log.i(Logger.LOG_TAG_DOWNLOAD, "retrying publicKeyUsable for $retryCount") + publicKeyUsable(retryCount + 1) + } else { + Log.i(Logger.LOG_TAG_DOWNLOAD, "retry count exceeded for publicKeyUsable") + works + } + } + + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 } suspend fun isPaymentInitiated(): Boolean { From f485031ceb8fe75e8a2f8090fc4351ead43eb164 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 14 Mar 2024 20:52:06 +0530 Subject: [PATCH 02/81] fix: doh url for google in constants --- app/src/main/java/com/celzero/bravedns/util/Constants.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/celzero/bravedns/util/Constants.kt b/app/src/main/java/com/celzero/bravedns/util/Constants.kt index a3e70c48c..723df023a 100644 --- a/app/src/main/java/com/celzero/bravedns/util/Constants.kt +++ b/app/src/main/java/com/celzero/bravedns/util/Constants.kt @@ -284,11 +284,12 @@ class Constants { const val BLOCK_FREE_DNS_MAX = "https://max.rethinkdns.com/dns-query" const val BLOCK_FREE_DNS_SKY = "https://sky.rethinkdns.com/dns-query" + // all fallback dns servers should have the corresponding ip address in servers.xml val DEFAULT_DNS_LIST = listOf( DefaultDnsServer(0, "None", "", "None"), DefaultDnsServer(1, "Rethink", "https://zero.rethinkdns.com/dns-query", "DoH"), - DefaultDnsServer(2, "Google", "https://dns.google.com/dns-query", "DoH"), + DefaultDnsServer(2, "Google", "https://dns.google/dns-query", "DoH"), DefaultDnsServer(3, "Cloudflare", "https://cloudflare-dns.com/dns-query", "DoH") ) From c092a73afbacfb7dcf3ff4740ea628724a56728a Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 14 Mar 2024 20:53:25 +0530 Subject: [PATCH 03/81] fix: init pagingConfig in viewModel --- .../viewmodel/ConnectionTrackerViewModel.kt | 21 ++++++++------- .../bravedns/viewmodel/DnsLogViewModel.kt | 14 +++++----- .../bravedns/viewmodel/RethinkLogViewModel.kt | 27 ++++++++----------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/ConnectionTrackerViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/ConnectionTrackerViewModel.kt index 34cbbcd6c..90593c4de 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/ConnectionTrackerViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/ConnectionTrackerViewModel.kt @@ -43,22 +43,23 @@ class ConnectionTrackerViewModel(private val connectionTrackerDAO: ConnectionTra BLOCKED(2) } + private val pagingConfig: PagingConfig + init { filterString.value = "" + pagingConfig = + PagingConfig( + enablePlaceholders = true, + prefetchDistance = 3, + initialLoadSize = LIVEDATA_PAGE_SIZE * 2, + maxSize = LIVEDATA_PAGE_SIZE * 3, + pageSize = LIVEDATA_PAGE_SIZE * 2, + jumpThreshold = 5 + ) } val connectionTrackerList = filterString.switchMap { input -> fetchNetworkLogs(input) } - private val pagingConfig = - PagingConfig( - enablePlaceholders = true, - prefetchDistance = 3, - initialLoadSize = LIVEDATA_PAGE_SIZE * 2, - maxSize = LIVEDATA_PAGE_SIZE * 3, - pageSize = LIVEDATA_PAGE_SIZE * 2, - jumpThreshold = 5 - ) - fun setFilter(searchString: String, filter: Set, type: TopLevelFilter) { filterRules.clear() diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt index 336a92219..394be128b 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt @@ -35,18 +35,18 @@ class DnsLogViewModel(private val dnsLogDAO: DnsLogDAO) : ViewModel() { private var filteredList: MutableLiveData = MutableLiveData() private var filterType = DnsLogFragment.DnsLogFilter.ALL - private val pagingConfig = - PagingConfig( + private val pagingConfig: PagingConfig + + init { + filteredList.value = "" + pagingConfig = PagingConfig( enablePlaceholders = true, prefetchDistance = 3, initialLoadSize = LIVEDATA_PAGE_SIZE * 2, - maxSize = LIVEDATA_PAGE_SIZE * 2, - pageSize = LIVEDATA_PAGE_SIZE, + maxSize = LIVEDATA_PAGE_SIZE * 3, + pageSize = LIVEDATA_PAGE_SIZE * 2, jumpThreshold = 5 ) - - init { - filteredList.value = "" } val dnsLogsList = filteredList.switchMap { input -> fetchDnsLogs(input) } diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/RethinkLogViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/RethinkLogViewModel.kt index 801372c68..333fdf353 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/RethinkLogViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/RethinkLogViewModel.kt @@ -32,29 +32,24 @@ import com.celzero.bravedns.util.Constants.Companion.LIVEDATA_PAGE_SIZE class RethinkLogViewModel(private val rlogDao: RethinkLogDao) : ViewModel() { private var filterString: MutableLiveData = MutableLiveData() - - enum class TopLevelFilter(val id: Int) { - ALL(0), - ALLOWED(1), - BLOCKED(2) - } + private val pagingConfig: PagingConfig init { filterString.value = "" + + pagingConfig = + PagingConfig( + enablePlaceholders = true, + prefetchDistance = 3, + initialLoadSize = LIVEDATA_PAGE_SIZE * 2, + maxSize = LIVEDATA_PAGE_SIZE * 3, + pageSize = LIVEDATA_PAGE_SIZE * 2, + jumpThreshold = 5 + ) } val rlogList = filterString.switchMap { input -> fetchNetworkLogs(input) } - private val pagingConfig = - PagingConfig( - enablePlaceholders = true, - prefetchDistance = 3, - initialLoadSize = LIVEDATA_PAGE_SIZE * 2, - maxSize = LIVEDATA_PAGE_SIZE * 3, - pageSize = LIVEDATA_PAGE_SIZE * 2, - jumpThreshold = 5 - ) - fun setFilter(searchString: String) { if (searchString.isNotBlank()) filterString.value = searchString else filterString.value = "" From 9f9b9486e3f57503af53a41ceebfae22651028da Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 14 Mar 2024 20:53:41 +0530 Subject: [PATCH 04/81] bump firestack for v055d --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e9e772a4b..b8a43a45c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,8 +247,8 @@ dependencies { fullImplementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9' // from: https://jitpack.io/#celzero/firestack - download 'com.github.celzero:firestack:21000918a3@aar' - implementation 'com.github.celzero:firestack:21000918a3@aar' + download 'com.github.celzero:firestack:ee9609571f@aar' + implementation 'com.github.celzero:firestack:ee9609571f@aar' // Work manager implementation('androidx.work:work-runtime-ktx:2.9.0') { From 5e0f1aaa7faf0617a18e2ea6df9137356b9588bd Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 14 Mar 2024 21:29:42 +0530 Subject: [PATCH 05/81] fix: get dns status for appropriate id --- .../ui/fragment/HomeScreenFragment.kt | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index f0ddecb93..0bd1f8bac 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -511,37 +511,38 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { } } - private var retryCountForDnsStatus: Int = 0 - private fun updateUiWithDnsStates(dnsName: String) { + val preferredId = if (appConfig.isSystemDns()) Backend.System else Backend.Preferred // get the status from go to check if the dns transport is added or not val id = - if (WireguardManager.oneWireGuardEnabled()) { - val id = WireguardManager.getOneWireGuardProxyId() ?: Backend.Preferred - "${ProxyManager.ID_WG_BASE}${id}" + if (WireguardManager.oneWireGuardEnabled() && persistentState.proxyDns) { + val id = WireguardManager.getOneWireGuardProxyId() + if (id == null) { + preferredId + } else { + "${ProxyManager.ID_WG_BASE}${id}" + } } else { - Backend.Preferred + preferredId } if (VpnController.isOn()) { - val status = VpnController.getDnsStatus(id) - // status null means the dns transport is not available / different id is usedE - if (status == null) { - if (retryCountForDnsStatus < 5) { - retryCountForDnsStatus++ - delay(TimeUnit.SECONDS.toMillis(1), lifecycleScope) { - if (isAdded) { - updateUiWithDnsStates(dnsName) + ui("dnsStatusCheck") { + repeat(5) { + val status = VpnController.getDnsStatus(id) + if (status != null) { + if (status == Backend.TOK) { + b.fhsCardDnsLatency.visibility = View.VISIBLE + b.fhsCardDnsFailure.visibility = View.GONE + return@ui } } + // status null means the dns transport is not available / different id is used + kotlinx.coroutines.delay(1000L) } b.fhsCardDnsLatency.visibility = View.GONE b.fhsCardDnsFailure.visibility = View.VISIBLE b.fhsCardDnsFailure.text = getString(R.string.failed_using_default) - b.fhsCardDnsLatency.isSelected = true - } else { - b.fhsCardDnsLatency.visibility = View.VISIBLE - b.fhsCardDnsFailure.visibility = View.GONE } } b.fhsCardDnsConnectedDns.text = dnsName From 736a4a4034112dda89f1f65249416e3caafd379b Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 16:24:01 +0530 Subject: [PATCH 06/81] continuous status updates for selected DNS and proxy --- .../adapter/DnsCryptEndpointAdapter.kt | 33 ++++++- .../bravedns/adapter/DoTEndpointAdapter.kt | 37 +++++++- .../bravedns/adapter/DohEndpointAdapter.kt | 36 ++++++- .../bravedns/adapter/ODoHEndpointAdapter.kt | 36 ++++++- .../bravedns/adapter/OneWgConfigAdapter.kt | 53 +++++++++-- .../adapter/RethinkEndpointAdapter.kt | 38 ++++++-- .../bravedns/adapter/WgConfigAdapter.kt | 94 ++++++++++++++++--- 7 files changed, 281 insertions(+), 46 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt index 95dae2cdb..77285d108 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt @@ -39,6 +39,8 @@ import com.celzero.bravedns.util.UIUtils.clipboardCopy import com.celzero.bravedns.util.Utilities import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -50,8 +52,10 @@ class DnsCryptEndpointAdapter( PagingDataAdapter( DIFF_CALLBACK ) { + var statusCheckJob: Job = Job() companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { @@ -115,9 +119,7 @@ class DnsCryptEndpointAdapter( b.dnsCryptEndpointListActionImage.isChecked = endpoint.isSelected if (endpoint.isSelected) { - // update the status after 1 second - val scope = (context as LifecycleOwner).lifecycleScope - Utilities.delay(1000L, scope) { updateSelectedStatus() } + keepSelectedStatusUpdated() } else { b.dnsCryptEndpointListUrlExplanation.text = "" } @@ -133,7 +135,26 @@ class DnsCryptEndpointAdapter( } } + private fun keepSelectedStatusUpdated() { + statusCheckJob = ui { + while (true) { + updateSelectedStatus() + delay(DELAY) + } + } + } + private fun updateSelectedStatus() { + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } + // always use the id as Dnsx.Preffered as it is the primary dns id for now val state = VpnController.getDnsStatus(Backend.Preferred) val status = UIUtils.getDnsStatusStringRes(state) @@ -217,6 +238,8 @@ class DnsCryptEndpointAdapter( endpoint.isSelected = true appConfig.handleDnscryptChanges(endpoint) } + // cancel the current job, new job will be created when the view is active + statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -236,6 +259,10 @@ class DnsCryptEndpointAdapter( withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job { + return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + } + private fun io(f: suspend () -> Unit) { lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt index f7f640fcf..46868bded 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt @@ -41,6 +41,8 @@ import com.celzero.bravedns.util.UIUtils.getDnsStatusStringRes import com.celzero.bravedns.util.Utilities import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -50,7 +52,10 @@ class DoTEndpointAdapter( private val appConfig: AppConfig ) : PagingDataAdapter(DIFF_CALLBACK) { + var statusCheckJob: Job = Job() + companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -107,23 +112,41 @@ class DoTEndpointAdapter( context.getString(R.string.lbl_insecure) ) } - b.endpointDesc.text = "" b.endpointCheck.isChecked = endpoint.isSelected Log.i( LOG_TAG_DNS, "connected to dot: ${endpoint.name} isSelected? ${endpoint.isSelected}" ) if (endpoint.isSelected) { - // update the status after 1 second - val scope = (context as LifecycleOwner).lifecycleScope - Utilities.delay(1000L, scope) { updateSelectedStatus() } + keepSelectedStatusUpdated() + } else { + b.endpointDesc.text = "" } // Shows either the info/delete icon for the DoH entries. showIcon(endpoint) } + private fun keepSelectedStatusUpdated() { + statusCheckJob = ui { + while (true) { + updateSelectedStatus() + delay(DELAY) + } + } + } + private fun updateSelectedStatus() { + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } + // always use the id as Dnsx.Preffered as it is the primary dns id for now val state = VpnController.getDnsStatus(Backend.Preferred) val status = getDnsStatusStringRes(state) @@ -152,6 +175,8 @@ class DoTEndpointAdapter( endpoint.isSelected = true appConfig.handleDoTChanges(endpoint) } + // cancel the current job, new job will be created when the view is active + statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -231,6 +256,10 @@ class DoTEndpointAdapter( withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job { + return lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.Main) { f() } } + } + private fun io(f: suspend () -> Unit) { lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.IO) { f() } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt index 0dae29422..2ff92fd00 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt @@ -42,6 +42,7 @@ import com.celzero.bravedns.util.Utilities import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -51,7 +52,10 @@ class DohEndpointAdapter( private val appConfig: AppConfig ) : PagingDataAdapter(DIFF_CALLBACK) { + var statusCheckJob: Job = Job() + companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -108,23 +112,41 @@ class DohEndpointAdapter( context.getString(R.string.lbl_insecure) ) } - b.endpointDesc.text = "" b.endpointCheck.isChecked = endpoint.isSelected Log.i( LOG_TAG_DNS, "connected to doh: ${endpoint.dohName} isSelected? ${endpoint.isSelected}" ) if (endpoint.isSelected) { - // update the status after 1 second - val scope = (context as LifecycleOwner).lifecycleScope - Utilities.delay(1000L, scope) { updateSelectedStatus() } + keepSelectedStatusUpdated() + } else { + b.endpointDesc.text = "" } // Shows either the info/delete icon for the DoH entries. showIcon(endpoint) } + private fun keepSelectedStatusUpdated() { + statusCheckJob = ui { + while (true) { + updateSelectedStatus() + delay(DELAY) + } + } + } + private fun updateSelectedStatus() { + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } + // always use the id as Dnsx.Preffered as it is the primary dns id for now val state = VpnController.getDnsStatus(Backend.Preferred) val status = getDnsStatusStringRes(state) @@ -153,6 +175,8 @@ class DohEndpointAdapter( endpoint.isSelected = true appConfig.handleDoHChanges(endpoint) } + // cancel the current job, new job will be created when the view is active + statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -232,6 +256,10 @@ class DohEndpointAdapter( withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job { + return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + } + private fun io(f: suspend () -> Unit) { lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt index e6ece0b50..43e6d99ae 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt @@ -41,6 +41,8 @@ import com.celzero.bravedns.util.UIUtils.getDnsStatusStringRes import com.celzero.bravedns.util.Utilities import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -50,7 +52,9 @@ class ODoHEndpointAdapter( private val appConfig: AppConfig ) : PagingDataAdapter(DIFF_CALLBACK) { + var statusCheckJob: Job = Job() companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -98,23 +102,41 @@ class ODoHEndpointAdapter( private fun displayDetails(endpoint: ODoHEndpoint) { b.endpointName.text = endpoint.name - b.endpointDesc.text = "" b.endpointCheck.isChecked = endpoint.isSelected Log.i( LOG_TAG_DNS, "connected to ODoH: ${endpoint.name} isSelected? ${endpoint.isSelected}" ) if (endpoint.isSelected) { - // update the status after 1 second - val scope = (context as LifecycleOwner).lifecycleScope - Utilities.delay(1000L, scope) { updateSelectedStatus() } + keepSelectedStatusUpdated() + } else { + b.endpointDesc.text = "" } // Shows either the info/delete icon for the DoH entries. showIcon(endpoint) } + private fun keepSelectedStatusUpdated() { + statusCheckJob = ui { + while (true) { + updateSelectedStatus() + delay(DELAY) + } + } + } + private fun updateSelectedStatus() { + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } + // always use the id as Dnsx.Preffered as it is the primary dns id for now val state = VpnController.getDnsStatus(Backend.Preferred) val status = getDnsStatusStringRes(state) @@ -143,6 +165,8 @@ class ODoHEndpointAdapter( endpoint.isSelected = true appConfig.handleODoHChanges(endpoint) } + // cancel the current job, new job will be created when the view is active + statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -233,6 +257,10 @@ class ODoHEndpointAdapter( withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job { + return lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.Main) { f() } } + } + private fun io(f: suspend () -> Unit) { lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.IO) { f() } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt index c59e345e3..75a85dd98 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt @@ -17,7 +17,6 @@ package com.celzero.bravedns.adapter import android.content.Context import android.content.Intent -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -41,13 +40,17 @@ import com.celzero.bravedns.util.UIUtils import com.celzero.bravedns.util.UIUtils.fetchColor import com.celzero.bravedns.util.Utilities import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class OneWgConfigAdapter(private val context: Context) : +class OneWgConfigAdapter(private val context: Context, private val lifecycleOwner: LifecycleOwner) : PagingDataAdapter(DIFF_CALLBACK) { + private var statusCheckJob: Job = Job() companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { @@ -94,6 +97,25 @@ class OneWgConfigAdapter(private val context: Context) : b.oneWgCheck.isChecked = config.isActive updateStatus(config) setupClickListeners(config) + if (config.oneWireGuard) { + keepStatusUpdated(config) + } else { + statusCheckJob.cancel() + b.interfaceDetailCard.strokeWidth = 0 + b.interfaceAppsCount.visibility = View.GONE + b.oneWgCheck.isChecked = false + b.interfaceStatus.text = + context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) + } + } + + private fun keepStatusUpdated(config: WgConfigFiles) { + statusCheckJob = ui { + while (true) { + updateStatus(config) + delay(DELAY) + } + } } private fun updateStatus(config: WgConfigFiles) { @@ -114,6 +136,16 @@ class OneWgConfigAdapter(private val context: Context) : } private fun updateStatusUi(config: WgConfigFiles, statusId: Long?, apps: String) { + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } + val appsCount = context.getString(R.string.firewall_card_status_active, apps) if (config.isActive) { b.interfaceDetailCard.strokeColor = fetchColor(context, R.color.accentGood) @@ -127,7 +159,9 @@ class OneWgConfigAdapter(private val context: Context) : if (statusId == Backend.TOK) { b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextPositive) - } else if (statusId == Backend.TUP) { + // cancel the job, as the status is connected + statusCheckJob.cancel() + } else if (statusId == Backend.TUP || statusId == Backend.TZZ) { b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextNeutral) } else { @@ -141,7 +175,7 @@ class OneWgConfigAdapter(private val context: Context) : context.getString( R.string.about_version_install_source, context - .getString(R.string.status_failing) + .getString(R.string.status_waiting) .replaceFirstChar(Char::titlecase), appsCount ) @@ -158,13 +192,8 @@ class OneWgConfigAdapter(private val context: Context) : fun setupClickListeners(config: WgConfigFiles) { b.interfaceDetailCard.setOnClickListener { launchConfigDetail(config.id) } - // b.oneWgCheck.setOnCheckedChangeListener(null) b.oneWgCheck.setOnClickListener { val isChecked = b.oneWgCheck.isChecked - Log.d( - "OneWgConfigAdapter", - "Switch checked: $isChecked, ${b.oneWgCheck.isChecked} W: ${WireguardManager.canEnableConfig(config)}" - ) io { if (isChecked) { if (WireguardManager.canEnableConfig(config)) { @@ -205,7 +234,11 @@ class OneWgConfigAdapter(private val context: Context) : withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job { + return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + } + private fun io(f: suspend () -> Unit) { - (context as LifecycleOwner).lifecycleScope.launch(Dispatchers.IO) { f() } + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt index 4dd31786f..7ded52f82 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt @@ -44,8 +44,9 @@ import com.celzero.bravedns.util.UIUtils.clipboardCopy import com.celzero.bravedns.util.Utilities import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext class RethinkEndpointAdapter( private val context: Context, @@ -56,7 +57,9 @@ class RethinkEndpointAdapter( DIFF_CALLBACK ) { + var statusCheckJob: Job = Job() companion object { + private const val DELAY = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -108,8 +111,6 @@ class RethinkEndpointAdapter( private fun displayDetails(endpoint: RethinkDnsEndpoint) { b.rethinkEndpointListUrlName.text = endpoint.name - // set empty for now, will be updated later, see updateBlocklistStatusText() - b.rethinkEndpointListUrlExplanation.text = "" b.rethinkEndpointListCheckImage.isChecked = endpoint.isActive Log.i( LOG_TAG_DNS, @@ -120,16 +121,31 @@ class RethinkEndpointAdapter( showIcon(endpoint) if (endpoint.isActive) { - // update the status after 1 second - val scope = (context as LifecycleOwner).lifecycleScope - Utilities.delay(1000L, scope) { updateBlocklistStatusText(endpoint) } + keepSelectedStatusUpdated(endpoint) } else { - updateBlocklistStatusText(endpoint) + b.rethinkEndpointListUrlExplanation.text = "" + } + } + + private fun keepSelectedStatusUpdated(endpoint: RethinkDnsEndpoint) { + ui { + while (true) { + updateBlocklistStatusText(endpoint) + delay(DELAY) + } } } private fun updateBlocklistStatusText(endpoint: RethinkDnsEndpoint) { - if (!endpoint.isActive) return + // if the view is not active then cancel the job + if ( + !lifecycleOwner.lifecycle.currentState.isAtLeast( + androidx.lifecycle.Lifecycle.State.STARTED + ) + ) { + statusCheckJob.cancel() + return + } val state = VpnController.getDnsStatus(Backend.Preferred) val status = UIUtils.getDnsStatusStringRes(state) @@ -174,6 +190,8 @@ class RethinkEndpointAdapter( endpoint.isActive = true appConfig.handleRethinkChanges(endpoint) } + // cancel the current job, new job will be created when the view is active + statusCheckJob.cancel() } private fun showDohMetadataDialog(endpoint: RethinkDnsEndpoint) { @@ -222,8 +240,8 @@ class RethinkEndpointAdapter( context.startActivity(intent) } - private suspend fun uiCtx(f: suspend () -> Unit) { - withContext(Dispatchers.Main) { f() } + private fun ui(f: suspend () -> Unit) { + lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } } private fun io(f: suspend () -> Unit) { diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt index 4ab750113..016a714c3 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt @@ -22,6 +22,7 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -37,11 +38,17 @@ import com.celzero.bravedns.ui.activity.WgConfigDetailActivity import com.celzero.bravedns.ui.activity.WgConfigEditorActivity.Companion.INTENT_EXTRA_WG_ID import com.celzero.bravedns.util.UIUtils import com.celzero.bravedns.util.Utilities -import com.celzero.bravedns.util.Utilities.delay +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class WgConfigAdapter(private val context: Context) : PagingDataAdapter(DIFF_CALLBACK) { + private var configs: MutableMap = mutableMapOf() + private var lifecycleOwner: LifecycleOwner? = null + companion object { private const val DELAY = 1000L private val DIFF_CALLBACK = @@ -80,23 +87,87 @@ class WgConfigAdapter(private val context: Context) : parent, false ) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return WgInterfaceViewHolder(itemBinding) } + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.onDetachedFromRecyclerView(recyclerView) + configs.values.forEach { + it.cancel() + } + configs.clear() + } + inner class WgInterfaceViewHolder(private val b: ListItemWgGeneralInterfaceBinding) : RecyclerView.ViewHolder(b.root) { fun update(config: WgConfigFiles) { b.interfaceNameText.text = config.name b.interfaceSwitch.isChecked = config.isActive - updateStatus(config) setupClickListeners(config) + updateStatusJob(config) + } + + private fun updateStatusJob(config: WgConfigFiles) { + if (config.isActive) { + val job = updateProxyStatusContinuously(config) + if (job != null) { + // cancel the job if it already exists for the same config + cancelJobIfAny(config.id) + configs[config.id] = job + } + } else { + b.interfaceCatchAll.visibility = View.GONE + b.interfaceLockdown.visibility = View.GONE + b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.background) + b.interfaceDetailCard.strokeWidth = 0 + b.interfaceSwitch.isChecked = false + b.interfaceStatus.text = + context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) + // cancel the job if it already exists for the config, as the config is disabled + cancelJobIfAny(config.id) + } + } + + private fun updateProxyStatusContinuously(config: WgConfigFiles): Job? { + return ui { + while (true) { + updateStatus(config) + delay(DELAY) + } + } + } + + private fun cancelJobIfAny(id: Int) { + val job = configs[id] + job?.cancel() + configs.remove(id) + } + + private fun cancelAllJobs() { + configs.values.forEach { + it.cancel() + } + configs.clear() } private fun updateStatus(config: WgConfigFiles) { val id = ProxyManager.ID_WG_BASE + config.id val appsCount = ProxyManager.getAppCountForProxy(id) val statusId = VpnController.getProxyStatusById(id) + + // if the view is not active then cancel the job + if ( + lifecycleOwner != null && + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false + ) { + cancelAllJobs() + return + } updateUi(config, appsCount) updateStatusUi(config, statusId) } @@ -131,8 +202,6 @@ class WgConfigAdapter(private val context: Context) : } private fun updateStatusUi(config: WgConfigFiles, statusId: Long?) { - if (context !is LifecycleOwner) return - if (config.isActive) { b.interfaceSwitch.isChecked = true b.interfaceDetailCard.strokeWidth = 2 @@ -142,7 +211,8 @@ class WgConfigAdapter(private val context: Context) : if (statusId == Backend.TOK) { b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentGood) - } else if (statusId == Backend.TUP) { + cancelJobIfAny(config.id) + } else if (statusId == Backend.TUP || statusId == Backend.TZZ) { b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.chipTextNeutral) } else { @@ -155,7 +225,7 @@ class WgConfigAdapter(private val context: Context) : b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) b.interfaceStatus.text = - context.getString(R.string.status_failing).replaceFirstChar(Char::titlecase) + context.getString(R.string.status_waiting).replaceFirstChar(Char::titlecase) } } else { b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.background) @@ -171,12 +241,9 @@ class WgConfigAdapter(private val context: Context) : b.interfaceSwitch.setOnCheckedChangeListener(null) b.interfaceSwitch.setOnClickListener { - val scope = (context as LifecycleOwner).lifecycleScope if (b.interfaceSwitch.isChecked) { if (WireguardManager.canEnableConfig(config)) { WireguardManager.enableConfig(config) - // update the status after 1 second - delay(DELAY, scope) { updateStatus(config) } } else { Utilities.showToastUiCentered( context, @@ -188,8 +255,6 @@ class WgConfigAdapter(private val context: Context) : } else { if (WireguardManager.canDisableConfig(config)) { WireguardManager.disableConfig(config) - // update the status after 1 second - delay(DELAY, scope) { updateStatus(config) } } else { Utilities.showToastUiCentered( context, @@ -212,4 +277,11 @@ class WgConfigAdapter(private val context: Context) : context.startActivity(intent) } } + + private fun ui(f: suspend () -> Unit): Job? { + if (lifecycleOwner == null) { + return null + } + return lifecycleOwner?.lifecycleScope?.launch(Dispatchers.Main) { f() } + } } From d699ab762bdb3cca77fbe616e96ad7c82c73ec89 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 16:25:04 +0530 Subject: [PATCH 07/81] fix: status updates for selected proxy --- .../java/com/celzero/bravedns/ui/activity/WgMainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt index 8015df6a5..c1fd9bd58 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt @@ -215,7 +215,7 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { val layoutManager = LinearLayoutManager(this) b.oneWgInterfaceList.layoutManager = layoutManager - oneWgConfigAdapter = OneWgConfigAdapter(this) + oneWgConfigAdapter = OneWgConfigAdapter(this, this) wgConfigViewModel.interfaces.observe(this) { oneWgConfigAdapter?.submitData(lifecycle, it) } b.oneWgInterfaceList.adapter = oneWgConfigAdapter } From 7ce873457bf9a5600aecabb48329a01174a4b369 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 17:00:51 +0530 Subject: [PATCH 08/81] fix: revamp custom ip rules handling remove HostName usage from IP rules impl splitHostPort, getIpNetPort (translated from go) --- .../bravedns/adapter/CustomIpAdapter.kt | 33 +-- .../bottomsheet/AppConnectionBottomSheet.kt | 5 +- .../ui/bottomsheet/ConnTrackerBottomSheet.kt | 5 +- .../bravedns/ui/fragment/CustomIpFragment.kt | 25 +- .../com/celzero/bravedns/database/CustomIp.kt | 60 +--- .../celzero/bravedns/database/CustomIpDao.kt | 2 +- .../bravedns/database/CustomIpRepository.kt | 4 +- .../bravedns/service/IpRulesManager.kt | 266 +++++++++++++++--- 8 files changed, 260 insertions(+), 140 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt index 5f9832374..a52e29bcb 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt @@ -54,6 +54,7 @@ import com.celzero.bravedns.util.Utilities.getFlag import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.dialog.MaterialAlertDialogBuilder +import inet.ipaddr.AddressStringException import inet.ipaddr.HostName import inet.ipaddr.HostNameException import inet.ipaddr.IPAddress @@ -739,9 +740,8 @@ class CustomIpAdapter(private val context: Context, private val type: CustomRule dBind.daciIpTitle.text = context.getString(R.string.ci_dialog_title) if (customIp.port != 0) { - val ipPort = - context.getString(R.string.ct_ip_port, customIp.ipAddress, customIp.port.toString()) - dBind.daciIpEditText.setText(ipPort) + val ipNetPort = IpRulesManager.joinIpNetPort(customIp.ipAddress, customIp.port) + dBind.daciIpEditText.setText(ipNetPort) } else { dBind.daciIpEditText.setText(customIp.ipAddress) } @@ -782,13 +782,14 @@ class CustomIpAdapter(private val context: Context, private val type: CustomRule ui { val input = dBind.daciIpEditText.text.toString() val ipString = Utilities.removeLeadingAndTrailingDots(input) - var hostName: HostName? = null var ip: IPAddress? = null + var port: Int? = null // chances of creating NetworkOnMainThread exception, handling with io operation ioCtx { - hostName = getHostName(ipString) - ip = hostName?.asAddress() + val ipPair = IpRulesManager.getIpNetPort(ipString) + ip = ipPair.first + port = ipPair.second } if (ip == null || ipString.isEmpty()) { @@ -798,27 +799,19 @@ class CustomIpAdapter(private val context: Context, private val type: CustomRule return@ui } - updateCustomIp(customIp, ipString, status) - } - } - - private fun getHostName(ip: String): HostName? { - return try { - val host = HostName(ip) - host.validate() - host - } catch (e: HostNameException) { - val ipAddress = IPAddressString(ip).address ?: return null - HostName(ipAddress) + updateCustomIp(customIp, ip, port, status) } } private fun updateCustomIp( prev: CustomIp, - ipString: String, + ipString: IPAddress?, + port: Int?, status: IpRulesManager.IpRuleStatus ) { - io { IpRulesManager.replaceIpRule(prev, ipString, status) } + if (ipString == null) return // invalid ip (ui error shown already) + + io { IpRulesManager.replaceIpRule(prev, ipString, port, status) } } private suspend fun ioCtx(f: suspend () -> Unit) { diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/AppConnectionBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/AppConnectionBottomSheet.kt index e283a1502..0834bceed 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/AppConnectionBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/AppConnectionBottomSheet.kt @@ -199,8 +199,11 @@ class AppConnectionBottomSheet : BottomSheetDialogFragment() { private fun applyIpRule(status: IpRulesManager.IpRuleStatus) { Log.i(Logger.LOG_TAG_FIREWALL, "ip rule for uid: $uid, ip: $ipAddress (${status.name})") ipRule = status + val ipPair = IpRulesManager.getIpNetPort(ipAddress) + val ip = ipPair.first ?: return + // set port number as null for all the rules applied from this screen - io { IpRulesManager.addIpRule(uid, ipAddress, null, status) } + io { IpRulesManager.addIpRule(uid, ip, null, status) } } override fun onDismiss(dialog: DialogInterface) { diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt index 97b519e46..7a4eea898 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt @@ -665,7 +665,10 @@ class ConnTrackerBottomSheet : BottomSheetDialogFragment(), KoinComponent { ipRuleStatus ) return@io - IpRulesManager.addIpRule(info!!.uid, cr.ipAddress, /*wildcard-port*/ 0, ipRuleStatus) + + val ipPair = IpRulesManager.getIpNetPort(info!!.ipAddress) + val ip = ipPair.first ?: return@io + IpRulesManager.addIpRule(info!!.uid, ip, /*wildcard-port*/ 0, ipRuleStatus) } } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/CustomIpFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/CustomIpFragment.kt index 9b9ae6e81..c0a98e4ec 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/CustomIpFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/CustomIpFragment.kt @@ -40,10 +40,7 @@ import com.celzero.bravedns.util.CustomLinearLayoutManager import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.viewmodel.CustomIpViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder -import inet.ipaddr.HostName -import inet.ipaddr.HostNameException import inet.ipaddr.IPAddress -import inet.ipaddr.IPAddressString import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -231,13 +228,14 @@ class CustomIpFragment : Fragment(R.layout.fragment_custom_ip), SearchView.OnQue ui { val input = dBind.daciIpEditText.text.toString() val ipString = Utilities.removeLeadingAndTrailingDots(input) - var hostName: HostName? = null var ip: IPAddress? = null + var port: Int = 0 // chances of creating NetworkOnMainThread exception, handling with io operation ioCtx { - hostName = getHostName(ipString) - ip = hostName?.asAddress() + val ipPair = IpRulesManager.getIpNetPort(ipString) + ip = ipPair.first + port = ipPair.second } if (ip == null || ipString.isEmpty()) { @@ -247,22 +245,11 @@ class CustomIpFragment : Fragment(R.layout.fragment_custom_ip), SearchView.OnQue } dBind.daciIpEditText.text.clear() - insertCustomIp(ipString, hostName?.port, status) + insertCustomIp(ip, port, status) } } - private suspend fun getHostName(ip: String): HostName? { - return try { - val host = HostName(ip) - host.validate() - host - } catch (e: HostNameException) { - val ipAddress = IPAddressString(ip).address ?: return null - HostName(ipAddress) - } - } - - private fun insertCustomIp(ip: String, port: Int?, status: IpRulesManager.IpRuleStatus) { + private fun insertCustomIp(ip: IPAddress?, port: Int?, status: IpRulesManager.IpRuleStatus) { if (ip == null) return io { IpRulesManager.addIpRule(uid, ip, port, status) } diff --git a/app/src/main/java/com/celzero/bravedns/database/CustomIp.kt b/app/src/main/java/com/celzero/bravedns/database/CustomIp.kt index b08fb2e26..54ddf48ac 100644 --- a/app/src/main/java/com/celzero/bravedns/database/CustomIp.kt +++ b/app/src/main/java/com/celzero/bravedns/database/CustomIp.kt @@ -15,13 +15,11 @@ */ package com.celzero.bravedns.database -import android.util.Log import androidx.room.Entity import com.celzero.bravedns.util.Constants.Companion.INIT_TIME_MS import com.celzero.bravedns.util.Constants.Companion.UID_EVERYBODY import com.celzero.bravedns.util.Constants.Companion.UNSPECIFIED_PORT -import com.celzero.bravedns.util.Logger -import inet.ipaddr.HostName +import inet.ipaddr.IPAddress import inet.ipaddr.IPAddressString /** @@ -59,57 +57,9 @@ class CustomIp { return result } - fun getCustomIpAddress(): HostName { - return if (port == UNSPECIFIED_PORT) { - HostName(ipAddress) - } else { - HostName(IPAddressString(ipAddress).address, port) - } - } - - // chances of null pointer exception while converting the string object to - // HostName().address ref: https://seancfoley.github.io/IPAddress/ - fun setCustomIpAddress(ipstr: String) { - var ip = ipstr - try { - if (HostName(ipstr).asAddress().isIPv4) { - ip = padIpv4Cidr(ipstr) - } - this.ipAddress = HostName(ip).asAddress().toNormalizedString() - } catch (ignored: NullPointerException) { - Log.e(Logger.LOG_TAG_VPN, "Invalid IP address added", ignored) - this.ipAddress = "" - } - } - - fun setCustomIpAddress(hostName: HostName) { - try { - this.ipAddress = hostName.asAddress().toNormalizedString() - val y = hostName.asAddress().assignPrefixForSingleBlock().toString() - val x = hostName.port - } catch (ignored: NullPointerException) { - Log.e(Logger.LOG_TAG_VPN, "Invalid IP address added", ignored) - this.ipAddress = "" - } - } - - private fun padIpv4Cidr(cidr: String): String { - // remove port number from the IP address - val ip = cidr.split(":")[0] - val plaincidr = ip.replace("[", "").replace("]", "") - val parts = plaincidr.split("/") - val ipParts = parts[0].split(".").toMutableList() - if (ipParts.size == 4) { - return cidr - } - // Pad the IP address with zeros if not fully specified - while (ipParts.size < 4) { - ipParts.add("*") - } - // Reassemble the IP address - val paddedIp = ipParts.joinToString(".") - // Reassemble the CIDR string - if (parts.size < 2) return paddedIp - return "$paddedIp/${parts[1]}" + fun getCustomIpAddress(): Pair { + val ip = IPAddressString(ipAddress).address + val port = port + return Pair(ip, port) } } diff --git a/app/src/main/java/com/celzero/bravedns/database/CustomIpDao.kt b/app/src/main/java/com/celzero/bravedns/database/CustomIpDao.kt index 830869424..31cdbc0ad 100644 --- a/app/src/main/java/com/celzero/bravedns/database/CustomIpDao.kt +++ b/app/src/main/java/com/celzero/bravedns/database/CustomIpDao.kt @@ -66,7 +66,7 @@ interface CustomIpDao { @Transaction @Query("delete from CustomIp where ipAddress = :ipAddress and uid = :uid and port = :port") - fun deleteRule(uid: Int, ipAddress: String, port: Int) + fun deleteRule(uid: Int, ipAddress: String, port: Int): Int @Query("delete from CustomIp where uid = :uid") fun deleteRulesByUid(uid: Int) diff --git a/app/src/main/java/com/celzero/bravedns/database/CustomIpRepository.kt b/app/src/main/java/com/celzero/bravedns/database/CustomIpRepository.kt index 6bf8406cd..dcba9cbaa 100644 --- a/app/src/main/java/com/celzero/bravedns/database/CustomIpRepository.kt +++ b/app/src/main/java/com/celzero/bravedns/database/CustomIpRepository.kt @@ -39,8 +39,8 @@ class CustomIpRepository(private val customIpDao: CustomIpDao) { return customIpDao.getCustomIpDetail(uid, ipAddress, port) } - suspend fun deleteRule(uid: Int, ipAddress: String, port: Int) { - customIpDao.deleteRule(uid, ipAddress, port) + suspend fun deleteRule(uid: Int, ipAddress: String, port: Int): Int { + return customIpDao.deleteRule(uid, ipAddress, port) } suspend fun deleteRulesByUid(uid: Int) { diff --git a/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt b/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt index 1b7ce7c30..3dd21053c 100644 --- a/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt +++ b/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt @@ -24,10 +24,12 @@ import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.database.CustomIp import com.celzero.bravedns.database.CustomIpRepository import com.celzero.bravedns.util.Constants +import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_FIREWALL import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder -import inet.ipaddr.HostName +import inet.ipaddr.AddressStringException +import inet.ipaddr.IPAddress import inet.ipaddr.IPAddressString import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -42,7 +44,7 @@ object IpRulesManager : KoinComponent { private var iptree = Backend.newIpTree() // key-value object for ip look-up - data class CacheKey(val hostName: HostName, val uid: Int) + data class CacheKey(val ipNetPort: String, val uid: Int) // stores the response for the look-up request from the BraveVpnService // especially useful for storing results of subnetMatch() function as it is expensive @@ -98,9 +100,11 @@ object IpRulesManager : KoinComponent { suspend fun load(): Long { iptree.clear() db.getIpRules().forEach { - val ipstr = it.getCustomIpAddress().toNormalizedString() - val k = treeKey(ipstr) - val v = treeVal(it.uid, it.port, it.status) + val pair = it.getCustomIpAddress() + val ipaddr = pair.first + val port = pair.second + val k = normalize(ipaddr) + val v = treeVal(it.uid, port, it.status) if (!k.isNullOrEmpty()) { try { logd("iptree.add($k, $v)") @@ -117,8 +121,15 @@ object IpRulesManager : KoinComponent { return db.getCustomIpsLiveData() } - private fun treeKey(ipstr: String): String? { + private fun normalize(ipaddr: IPAddress?): String? { + if (ipaddr == null) return null + return treeKey(ipaddr.toNormalizedString()) + } + + private fun treeKey(ipstr: String?): String? { + if (ipstr == null) return null // "192/8" -> 0.0.0.192/32 + // "192.0.0.0" -> 192.0.0.0/32 // "*.*" -> 0.0.0.0/0 // "*.*.*.*" -> 0.0.0.0/0 // "0/24" -> 0.0.0.0/24 @@ -134,7 +145,13 @@ object IpRulesManager : KoinComponent { // 1.2.*.4 returns null // 1.2.252-255.* returns 1.2.252.0/22 // 1.2.3.4/x returns the same address - return ipaddr(ipstr)?.asAddress()?.assignPrefixForSingleBlock()?.toCanonicalString() + val pair = hostAddr(ipstr) + val ipAddr = pair.first + return if (ipstr.contains("*")) { + ipAddr.assignPrefixForSingleBlock()?.toCanonicalString() + } else { + ipAddr.toNormalizedString() + } } private fun treeValLike(uid: Int, port: Int): String { @@ -181,7 +198,8 @@ object IpRulesManager : KoinComponent { private suspend fun updateRule(uid: Int, ipaddr: String, port: Int, status: IpRuleStatus) { Log.i(LOG_TAG_FIREWALL, "ip rule, update: $ipaddr for uid: $uid; status: ${status.name}") - val c = makeCustomIp(uid, ipaddr, port, status) + // ipaddr is expected to be normalized + val c = makeCustomIp2(uid, ipaddr, port, status) db.update(c) val k = treeKey(ipaddr) if (!k.isNullOrEmpty()) { @@ -203,13 +221,14 @@ object IpRulesManager : KoinComponent { return updateRule(c.uid, c.ipAddress, c.port, IpRuleStatus.NONE) } - suspend fun updateBlock(customIp: CustomIp) { - return updateRule(customIp.uid, customIp.ipAddress, customIp.port, IpRuleStatus.BLOCK) + suspend fun updateBlock(c: CustomIp) { + return updateRule(c.uid, c.ipAddress, c.port, IpRuleStatus.BLOCK) } fun hasRule(uid: Int, ipstr: String, port: Int): IpRuleStatus { - val ip = ipaddr(ipstr, port) ?: return IpRuleStatus.NONE - val ck = CacheKey(ip, uid) + val pair = hostAddr(ipstr, port) + val ipNetPort = joinIpNetPort(normalize(pair.first) + pair.second) + val ck = CacheKey(ipNetPort, uid) resultsCache.getIfPresent(ck)?.let { // return only if both ip and app(uid) matches @@ -249,17 +268,10 @@ object IpRulesManager : KoinComponent { return IpRuleStatus.NONE } - fun ipaddr(ipstr: String, port: Int? = null): HostName? { - return try { - if (port == null) { - HostName(ipstr) - } else { - HostName(IPAddressString(ipstr).address, port) - } - } catch (e: Exception) { - Log.e(LOG_TAG_FIREWALL, "err ip-rule($ipstr, $port): ${e.message}") - null - } + private fun hostAddr(ipstr: String, p: Int? = null): Pair { + val ip: IPAddress = IPAddressString(ipstr).address + val port: Int = p ?: 0 + return Pair(ip, port) } fun getMostSpecificRuleMatch(uid: Int, ipstr: String, port: Int = 0): IpRuleStatus { @@ -284,6 +296,8 @@ object IpRulesManager : KoinComponent { // rules at the end of the list have higher precedence as they're more specific // (think: 0.0.0.0/0 vs 1.1.1.1/32) val x = iptree.valuesLike(k, vlike) + // ex: uid: 10169, k: 142.250.67.78, vlike: 10169:443 => x: 10169:443:0 + // (10169:443:0) => (uid : port : rule[0->none, 1-> block, 2 -> trust, 3 -> bypass]) logd("getMostSpecificRouteMatch: $uid, $k, $vlike => $x") return treeValsFromCsv(x) .map { treeValStatus(it) } @@ -294,10 +308,12 @@ object IpRulesManager : KoinComponent { suspend fun deleteRulesByUid(uid: Int) { db.getRulesByUid(uid).forEach { - val ipstr = it.getCustomIpAddress().toNormalizedString() - val k = treeKey(ipstr) + val pair = it.getCustomIpAddress() + val ipaddr = pair.first + val port = pair.second + val k = normalize(ipaddr) if (!k.isNullOrEmpty()) { - iptree.esc(k, treeVal(it.uid, it.port, it.status)) + iptree.esc(k, treeVal(it.uid, port, it.status)) } } db.deleteRulesByUid(uid) @@ -310,7 +326,7 @@ object IpRulesManager : KoinComponent { resultsCache.invalidateAll() } - private fun makeCustomIp( + private fun makeCustomIp2( uid: Int, ipAddress: String, port: Int?, @@ -318,7 +334,7 @@ object IpRulesManager : KoinComponent { wildcard: Boolean = false ): CustomIp { val customIp = CustomIp() - customIp.setCustomIpAddress(ipAddress) + customIp.ipAddress = ipAddress // empty for port-only rules, always normalized customIp.port = port ?: Constants.UNSPECIFIED_PORT customIp.protocol = "" customIp.isActive = true @@ -326,9 +342,12 @@ object IpRulesManager : KoinComponent { customIp.wildcard = wildcard customIp.modifiedDateTime = System.currentTimeMillis() + val pair = customIp.getCustomIpAddress() + val ipaddr = pair.first + val port = pair.second // TODO: is this needed in database? customIp.ruleType = - if (customIp.getCustomIpAddress().asAddress()?.isIPv6 == true) { + if (ipaddr.isIPv6) { IPRuleType.IPV6.id } else { IPRuleType.IPV4.id @@ -337,14 +356,73 @@ object IpRulesManager : KoinComponent { return customIp } - suspend fun addIpRule(uid: Int, ipstr: String, port: Int?, status: IpRuleStatus) { + // chances of null pointer exception while converting the string object to + // IPAddress().address ref: https://seancfoley.github.io/IPAddress/ + private fun padAndNormalize(ipaddr: IPAddress): String { + var ipStr: String = ipaddr.toNormalizedString() + try { + if (ipaddr.isIPv4) { + ipStr = padIpv4Cidr(ipaddr.toNormalizedString()) + } + val pair = hostAddr(ipStr) + return normalize(pair.first) ?: "" + } catch (ignored: NullPointerException) { + Log.e(Logger.LOG_TAG_VPN, "Invalid IP address added", ignored) + } + return "" // empty ips mean its a port-only rule + } + + private fun padIpv4Cidr(cidr: String): String { + // remove port number from the IP address + // [192.x.y/24]:80 + // ip => [192.x.y/24] + val ip = cidr.split(":")[0] + // plaincidr => 192.x.y/24 + val hasbraces = ip.contains("[") and ip.contains("]") + val plaincidr = ip.replace("[", "").replace("]", "") + // parts => [192.x.y, 24] + val parts = plaincidr.split("/") + // ipparts => [192, x, y] + val ipParts = parts[0].split(".").toMutableList() + if (ipParts.size == 4) { + return cidr + } + // Pad the IP address with zeros if not fully specified + while (ipParts.size < 4) { + // ipparts => [192, x, y, *] + ipParts.add("*") + } + // Remove the last part of the IP address if it is 0 + // 192.x.y.0 => 192.x.y.*; 192.x.0.* => 192.x.*.* + for (i in (ipParts.size - 1) downTo 0) { + if (ipParts[i] == "*") { + continue + } else if (ipParts[i] == "0") { + ipParts[i] = "*" + } else { + break + } + } + // Reassemble the IP address; paddedIp => 192.x.y.* + val paddedIp = ipParts.joinToString(".") + // Reassemble the CIDR string + if (parts.size == 1) return paddedIp + return if (hasbraces) { + "[$paddedIp/${parts[1]}]" + } else { + "$paddedIp/${parts[1]}" + } + } + + suspend fun addIpRule(uid: Int, ipstr: IPAddress, port: Int?, status: IpRuleStatus) { Log.i( LOG_TAG_FIREWALL, "ip rule, add rule for ($uid) ip: $ipstr, $port with status: ${status.name}" ) - val c = makeCustomIp(uid, ipstr, port, status) + val normalizedIp = padAndNormalize(ipstr) + val c = makeCustomIp2(uid, normalizedIp, port, status) db.insert(c) - val k = treeKey(ipstr) + val k = treeKey(normalizedIp) if (!k.isNullOrEmpty()) { iptree.escLike(k, treeValLike(uid, port ?: 0)) iptree.add(k, treeVal(uid, port ?: 0, status.id)) @@ -363,16 +441,29 @@ object IpRulesManager : KoinComponent { Log.i(LOG_TAG_FIREWALL, "ip rules updated") } - suspend fun replaceIpRule(prevRule: CustomIp, ipString: String, newStatus: IpRuleStatus) { - val host = HostName(ipString) - val prevIpAddrStr = prevRule.getCustomIpAddress().asAddress().toNormalizedString() - val newIpAddrStr = host.asAddress().toNormalizedString() + suspend fun replaceIpRule( + prevRule: CustomIp, + ipaddr: IPAddress, + port: Int?, + newStatus: IpRuleStatus + ) { + val pair = prevRule.getCustomIpAddress() + val prevIpaddr = pair.first + val prevPort = pair.second + val prevIpAddrStr = normalize(prevIpaddr) + val newIpAddrStr = padAndNormalize(ipaddr) Log.i( LOG_TAG_FIREWALL, - "ip rule, replace (${prevRule.uid}); ${prevIpAddrStr}:${prevRule.port}; new: $newIpAddrStr, ${newStatus.name}" + "ip rule, replace (${prevRule.uid}); ${prevIpAddrStr}:${prevPort}; new: $ipaddr:$port, ${newStatus.name}" ) - db.deleteRule(prevRule.uid, prevIpAddrStr, prevRule.port) - val newRule = makeCustomIp(prevRule.uid, ipString, host.port, newStatus) + if (prevIpAddrStr != null) { // prev addr should never be null + val isDeleted = db.deleteRule(prevRule.uid, prevIpAddrStr, prevRule.port) + if (isDeleted == 0) { + // delete didn't occur with normalized addr, use ip from prevRule obj + db.deleteRule(prevRule.uid, prevRule.ipAddress, prevRule.port) + } + } + val newRule = makeCustomIp2(prevRule.uid, newIpAddrStr, port, newStatus) db.insert(newRule) val pk = treeKey(prevIpAddrStr) if (!pk.isNullOrEmpty()) { @@ -380,9 +471,102 @@ object IpRulesManager : KoinComponent { } val nk = treeKey(newIpAddrStr) if (!nk.isNullOrEmpty()) { - iptree.escLike(nk, treeValLike(newRule.uid, host.port ?: 0)) - iptree.add(nk, treeVal(newRule.uid, host.port ?: 0, newStatus.id)) + iptree.escLike(nk, treeValLike(newRule.uid, port ?: 0)) + iptree.add(nk, treeVal(newRule.uid, port ?: 0, newStatus.id)) } resultsCache.invalidateAll() } + + // translated from go, net.SplitHostPort() + class AddrError(val err: String, val addr: String) : Exception() + + private fun splitHostPort(hostport: String): Triple { + val missingPort = "missing port in address" + val tooManyColons = "too many colons in address" + + fun addrErr(addr: String, why: String): Triple { + return Triple("", "", AddrError(why, addr)) + } + + var host = "" + var port = "" + var err: Exception? = null + var j = 0 + var k = 0 + + // The port starts after the last colon. + val i = hostport.lastIndexOf(':') + if (i < 0) { + return addrErr(hostport, missingPort) + } + + if (hostport[0] == '[') { + // Expect the first ']' just before the last ':'. + val end = hostport.indexOf(']') + if (end < 0) { + return addrErr(hostport, "missing ']' in address") + } + when (end + 1) { + hostport.length -> { + // There can't be a ':' behind the ']' now. + return addrErr(hostport, missingPort) + } + i -> { + // The expected result. + } + else -> { + // Either ']' isn't followed by a colon, or it is + // followed by a colon that is not the last one. + if (hostport[end + 1] == ':') { + return addrErr(hostport, tooManyColons) + } + return addrErr(hostport, missingPort) + } + } + host = hostport.substring(1, end) + j = 1 + k = end + 1 // there can't be a '[' resp. ']' before these positions + } else { + host = hostport.substring(0, i) + if (host.contains(':')) { + return addrErr(hostport, tooManyColons) + } + } + if (hostport.substring(j).contains('[')) { + return addrErr(hostport, "unexpected '[' in address") + } + if (hostport.substring(k).contains(']')) { + return addrErr(hostport, "unexpected ']' in address") + } + + port = hostport.substring(i + 1) + return Triple(host, port, err) + } + + fun getIpNetPort(inp: String): Pair { + val h = splitHostPort(inp) + var ipNet: IPAddress? = null + var port = 0 + if (h.first.isEmpty()) { + try { + val ips = IPAddressString(inp) + ips.validate() + ipNet = ips.address + } catch (e: AddressStringException) { + Log.w(LOG_TAG_FIREWALL, "err: getIpNetPort, ${e.message}", e) + } + } else { + ipNet = IPAddressString(h.first).address + port = h.second.toIntOrNull() ?: 0 + } + return Pair(ipNet, port) + } + + fun joinIpNetPort(ipNet: String, port: Int = 0): String { + return if (ipNet.contains(":") || ipNet.contains("/")) { + "[$ipNet]:$port" + } else { + "$ipNet:$port" + } + } } From 108c6c923670b3de8549e2f3d6d7e7b7347d8832 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 17:01:34 +0530 Subject: [PATCH 09/81] ui: material btn font size change --- .../full/res/layout/list_item_custom_all_domain.xml | 10 +++++----- app/src/full/res/layout/list_item_custom_all_ip.xml | 11 +++++------ app/src/full/res/layout/list_item_custom_domain.xml | 10 +++++----- app/src/full/res/layout/list_item_custom_ip.xml | 10 +++++----- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/src/full/res/layout/list_item_custom_all_domain.xml b/app/src/full/res/layout/list_item_custom_all_domain.xml index ca25d8780..f6bd8c38f 100644 --- a/app/src/full/res/layout/list_item_custom_all_domain.xml +++ b/app/src/full/res/layout/list_item_custom_all_domain.xml @@ -131,7 +131,7 @@ android:tag="0" android:text="@string/ci_no_rule" android:textColor="?attr/primaryTextColor" - android:textSize="@dimen/small_font_subheading_text" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> diff --git a/app/src/full/res/layout/list_item_custom_all_ip.xml b/app/src/full/res/layout/list_item_custom_all_ip.xml index 5061503d1..e344397c9 100644 --- a/app/src/full/res/layout/list_item_custom_all_ip.xml +++ b/app/src/full/res/layout/list_item_custom_all_ip.xml @@ -130,7 +130,6 @@ android:layout_height="wrap_content" android:layout_below="@id/custom_ip_container" android:gravity="center" - android:visibility="gone" app:selectionRequired="true" app:singleSelection="true"> @@ -143,7 +142,7 @@ android:tag="0" android:text="@string/ci_no_rule" android:textColor="?attr/primaryTextColor" - android:textSize="@dimen/small_font_subheading_text" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> diff --git a/app/src/full/res/layout/list_item_custom_domain.xml b/app/src/full/res/layout/list_item_custom_domain.xml index c579ec9fd..03274429c 100644 --- a/app/src/full/res/layout/list_item_custom_domain.xml +++ b/app/src/full/res/layout/list_item_custom_domain.xml @@ -96,7 +96,7 @@ android:tag="0" android:text="@string/ci_no_rule" android:textColor="?attr/primaryTextColor" - android:textSize="@dimen/small_font_subheading_text" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> diff --git a/app/src/full/res/layout/list_item_custom_ip.xml b/app/src/full/res/layout/list_item_custom_ip.xml index 2801dbc9e..3ee52a910 100644 --- a/app/src/full/res/layout/list_item_custom_ip.xml +++ b/app/src/full/res/layout/list_item_custom_ip.xml @@ -107,7 +107,7 @@ android:tag="0" android:text="@string/ci_no_rule" android:textColor="?attr/primaryTextColor" - android:textSize="@dimen/small_font_subheading_text" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> + android:textSize="@dimen/mini_font_text_view" /> From f9988c25d9ddcffb5b30623394a7a31e8deedca9 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 17:02:59 +0530 Subject: [PATCH 10/81] trie-based handling of trusted domains with Wildcards --- .../bravedns/service/DomainRulesManager.kt | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/DomainRulesManager.kt b/app/src/main/java/com/celzero/bravedns/service/DomainRulesManager.kt index e26b750da..a92cc65ea 100644 --- a/app/src/main/java/com/celzero/bravedns/service/DomainRulesManager.kt +++ b/app/src/main/java/com/celzero/bravedns/service/DomainRulesManager.kt @@ -21,7 +21,6 @@ import android.util.Patterns import androidx.lifecycle.LiveData import backend.Backend import com.celzero.bravedns.R -import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.database.CustomDomain import com.celzero.bravedns.database.CustomDomainRepository import com.celzero.bravedns.util.Constants @@ -39,8 +38,11 @@ object DomainRulesManager : KoinComponent { private val db by inject() private var trie: backend.RadixTree = Backend.newRadixTree() + // fixme: find a better way to handle trusted domains without using two data structures // map to store the trusted domains with set of uids private val trustedMap: MutableMap> = ConcurrentHashMap() + // even though we have trustedMap, we need to keep the trie for wildcard matching + private var trustedTrie: backend.RadixTree = Backend.newRadixTree() // regex to check if url is valid wildcard domain // valid wildcard domain: *.example.com, *.example.co.in, *.do-main.com @@ -93,7 +95,7 @@ object DomainRulesManager : KoinComponent { } // update the cache with the domain and its status based on the domain type - fun updateTrie(cd: CustomDomain) { + private fun updateTrie(cd: CustomDomain) { val key = mkTrieKey(cd.domain, cd.uid) trie.set(key, cd.status.toString()) } @@ -105,8 +107,15 @@ object DomainRulesManager : KoinComponent { return domain.lowercase(Locale.ROOT) + "," + uid } + private fun mkTrieKey(d: String): String { + // *.google.co.uk -> .google.co.uk + val domain = d.removePrefix("*") + return domain.lowercase(Locale.ROOT) + } + suspend fun load(): Long { trie.clear() + trustedTrie.clear() trustedMap.clear() db.getAllCustomDomains().forEach { cd -> val key = mkTrieKey(cd.domain, cd.uid) @@ -119,6 +128,8 @@ object DomainRulesManager : KoinComponent { private fun maybeAddToTrustedMap(cd: CustomDomain) { if (cd.status == Status.TRUST.id) { val domain = cd.domain.lowercase(Locale.ROOT) + val key = mkTrieKey(domain) + trustedTrie.set(key, cd.status.toString()) trustedMap[cd.domain] = trustedMap.getOrDefault(domain, emptySet()).plus(cd.uid) } } @@ -167,9 +178,7 @@ object DomainRulesManager : KoinComponent { return false } val domain = d.lowercase(Locale.ROOT) - val match = trustedMap.containsKey(domain) - if (DEBUG) Log.d(LOG_TAG_DNS, "isDomainTrusted: $domain: $match") - return match + return trustedTrie.hasAny(domain) } suspend fun trust(cd: CustomDomain) { @@ -230,6 +239,13 @@ object DomainRulesManager : KoinComponent { } else { trustedMap[d] = trustedMap.getOrDefault(d, emptySet()).minus(uid) } + if (trustedMap[d] == null) { + val key = mkTrieKey(d) + trustedTrie.del(key) + } else { + val key = mkTrieKey(d) + trustedTrie.set(key, status.toString()) + } } suspend fun updateDomainRule( @@ -269,6 +285,8 @@ object DomainRulesManager : KoinComponent { val trustedUids = trustedMap.getOrDefault(d, emptySet()).minus(uid) if (trustedUids.isEmpty()) { trustedMap.remove(d) + val key = mkTrieKey(d) + trustedTrie.del(key) } else { trustedMap[d] = trustedUids } @@ -279,6 +297,8 @@ object DomainRulesManager : KoinComponent { val newUids = uids.minus(uid) if (newUids.isEmpty()) { trustedMap.remove(domain) + val key = mkTrieKey(domain) + trustedTrie.del(key) } else { trustedMap[domain] = newUids } @@ -296,6 +316,7 @@ object DomainRulesManager : KoinComponent { db.deleteAllRules() trie.clear() trustedMap.clear() + trustedTrie.clear() } private fun removeFromTrie(cd: CustomDomain) { From f71862b208f92c25baaf03044e11379c89aad560 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 17:59:46 +0530 Subject: [PATCH 11/81] ui: do not show wireguard preshared key --- .../java/com/celzero/bravedns/adapter/WgPeersAdapter.kt | 6 ------ app/src/main/res/layout/list_item_wg_peers.xml | 2 ++ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt index 18b187ac0..ee98b2bd7 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt @@ -82,12 +82,6 @@ class WgPeersAdapter( b.persistentKeepaliveText.visibility = View.GONE b.persistentKeepaliveLabel.visibility = View.GONE } - if (wgPeer.getPreSharedKey().isPresent) { - b.preSharedKeyText.text = wgPeer.getPreSharedKey().get().base64() - } else { - b.preSharedKeyText.visibility = View.GONE - b.preSharedKeyLabel.visibility = View.GONE - } b.publicKeyText.text = wgPeer.getPublicKey().base64() b.peerEdit.setOnClickListener { openEditPeerDialog(wgPeer) } diff --git a/app/src/main/res/layout/list_item_wg_peers.xml b/app/src/main/res/layout/list_item_wg_peers.xml index 6ac1d4671..bd9d1550c 100644 --- a/app/src/main/res/layout/list_item_wg_peers.xml +++ b/app/src/main/res/layout/list_item_wg_peers.xml @@ -78,6 +78,7 @@ android:labelFor="@+id/pre_shared_key_text" android:text="@string/lbl_preshared_key" app:layout_constraintStart_toStartOf="parent" + android:visibility="gone" app:layout_constraintTop_toBottomOf="@id/public_key_text" /> From 6b8cdfb1cb630b4671121c05261b6d3bc38380ce Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 18:03:48 +0530 Subject: [PATCH 12/81] update mapping table with proxy name change --- .../full/java/com/celzero/bravedns/service/ProxyManager.kt | 4 ++++ .../celzero/bravedns/database/ProxyAppMappingRepository.kt | 4 ++++ .../celzero/bravedns/database/ProxyApplicationMappingDAO.kt | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt b/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt index 7f45a28dc..3b6643ff0 100644 --- a/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt @@ -151,6 +151,10 @@ object ProxyManager : KoinComponent { Log.i(LOG_TAG_PROXY, "added all apps to proxy: $proxyId") } + suspend fun updateProxyNameForProxyId(proxyId: String, proxyName: String) { + db.updateProxyNameForProxyId(proxyId, proxyName) + } + suspend fun setProxyIdForUnselectedApps(proxyId: String, proxyName: String) { // ID_NONE or empty proxy-id is not allowed if (!isValidProxyPrefix(proxyId)) { diff --git a/app/src/main/java/com/celzero/bravedns/database/ProxyAppMappingRepository.kt b/app/src/main/java/com/celzero/bravedns/database/ProxyAppMappingRepository.kt index 0758f4059..2ad34f6fa 100644 --- a/app/src/main/java/com/celzero/bravedns/database/ProxyAppMappingRepository.kt +++ b/app/src/main/java/com/celzero/bravedns/database/ProxyAppMappingRepository.kt @@ -59,6 +59,10 @@ class ProxyAppMappingRepository( proxyApplicationMappingDAO.updateProxyForAllApps(proxyId, proxyName) } + suspend fun updateProxyNameForProxyId(proxyId: String, proxyName: String) { + proxyApplicationMappingDAO.updateProxyNameForProxyId(proxyId, proxyName) + } + suspend fun updateProxyForUnselectedApps(proxyId: String, proxyName: String) { return proxyApplicationMappingDAO.updateProxyForUnselectedApps(proxyId, proxyName) } diff --git a/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt b/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt index f9d91a1ef..6ddf39248 100644 --- a/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt @@ -79,6 +79,11 @@ interface ProxyApplicationMappingDAO { @Query("update ProxyApplicationMapping set proxyId = :cfgId, proxyName = :cfgName") fun updateProxyForAllApps(cfgId: String, cfgName: String = "") + @Query( + "update ProxyApplicationMapping set proxyName = :proxyName where proxyId = :proxyId" + ) + fun updateProxyNameForProxyId(proxyId: String, proxyName: String) + @Query( "update ProxyApplicationMapping set proxyId = :cfgId, proxyName = :cfgName where proxyId = ''" ) From 2187896eae9f6e4d481a840f01782a1ad58aa514 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 18:11:36 +0530 Subject: [PATCH 13/81] ui: always show allowed for Rethink --- .../com/celzero/bravedns/adapter/FirewallAppListAdapter.kt | 4 +++- app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt | 4 ++-- .../main/java/com/celzero/bravedns/service/FirewallManager.kt | 3 +++ app/src/main/java/com/celzero/bravedns/util/Constants.kt | 2 ++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt index 5d95e632b..587eaa5be 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt @@ -43,10 +43,10 @@ import com.celzero.bravedns.ui.activity.AppInfoActivity.Companion.UID_INTENT_NAM import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.util.Utilities.getIcon import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.concurrent.TimeUnit class FirewallAppListAdapter( private val context: Context, @@ -107,6 +107,8 @@ class FirewallAppListAdapter( if (appInfo.packageName == context.packageName) { b.firewallAppToggleWifi.visibility = View.GONE b.firewallAppToggleMobileData.visibility = View.GONE + b.firewallAppToggleOther.text = + context.getString(R.string.firewall_status_allow) return@uiCtx } diff --git a/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt b/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt index 9e9819be1..bfa0f8040 100644 --- a/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt @@ -24,6 +24,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import com.celzero.bravedns.data.DataUsage +import com.celzero.bravedns.util.Constants.Companion.RETHINK_PACKAGE @Dao interface AppInfoDAO { @@ -31,8 +32,7 @@ interface AppInfoDAO { @Update fun update(appInfo: AppInfo): Int @Query( - "update AppInfo set firewallStatus = :firewallStatus, connectionStatus = :connectionStatus where uid = :uid" - ) + "update AppInfo set firewallStatus = :firewallStatus, connectionStatus = :connectionStatus where uid = :uid and packageName != 'com.celzero.bravedns'") fun updateFirewallStatusByUid(uid: Int, firewallStatus: Int, connectionStatus: Int) @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(appInfo: AppInfo): Long diff --git a/app/src/main/java/com/celzero/bravedns/service/FirewallManager.kt b/app/src/main/java/com/celzero/bravedns/service/FirewallManager.kt index 0ba40142d..45cca6750 100644 --- a/app/src/main/java/com/celzero/bravedns/service/FirewallManager.kt +++ b/app/src/main/java/com/celzero/bravedns/service/FirewallManager.kt @@ -27,6 +27,7 @@ import com.celzero.bravedns.service.FirewallManager.GlobalVariable.appInfos import com.celzero.bravedns.service.FirewallManager.GlobalVariable.appInfosLiveData import com.celzero.bravedns.service.FirewallManager.GlobalVariable.foregroundUids import com.celzero.bravedns.util.AndroidUidConfig +import com.celzero.bravedns.util.Constants.Companion.RETHINK_PACKAGE import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_FIREWALL import com.celzero.bravedns.util.OrbotHelper import com.google.common.collect.HashMultimap @@ -418,6 +419,8 @@ object FirewallManager : KoinComponent { ) { mutex.withLock { appInfos.get(uid).forEach { + if (it.packageName == RETHINK_PACKAGE) return@forEach + it.firewallStatus = firewallStatus.id it.connectionStatus = connectionStatus.id } diff --git a/app/src/main/java/com/celzero/bravedns/util/Constants.kt b/app/src/main/java/com/celzero/bravedns/util/Constants.kt index 723df023a..1fb6edcc3 100644 --- a/app/src/main/java/com/celzero/bravedns/util/Constants.kt +++ b/app/src/main/java/com/celzero/bravedns/util/Constants.kt @@ -49,6 +49,8 @@ class Constants { const val ONDEVICE_BLOCKLIST_UPDATE_CHECK_QUERYPART_1 = "update" const val ONDEVICE_BLOCKLIST_UPDATE_CHECK_QUERYPART_2 = "blocklists" + const val RETHINK_PACKAGE = "com.celzero.bravedns" + // url to check to check the if there is update available for on-device blocklist const val ONDEVICE_IPDB_UPDATE_CHECK_URL = "$DOWNLOAD_BASE_URL/update/geoip?$RETHINK_BLOCKLIST_CONFIGURE_URL_PARAMETER" From fdec4f0f21e9c70e361a4c563c28dc8e41450209 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 18:13:13 +0530 Subject: [PATCH 14/81] ui: set add/rmv apps ui to negative state on 0 apps --- .../celzero/bravedns/ui/activity/WgConfigDetailActivity.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt index 806ebb10f..fc133d30a 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt @@ -285,7 +285,10 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { val id = ProxyManager.ID_WG_BASE + configId b.applicationsBtn.isEnabled = true mappingViewModel.getAppCountById(id).observe(this) { - b.applicationsBtn.text = getString(R.string.add_remove_apps, it.toString()) + if (it == 0) { + b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentBad)) + } + b.applicationsBtn.text = getString(R.string.add_remove_apps, it.toString()) } } From bb96fafd30af5b42d7b6ef33a567220fa5a4364b Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 18:15:29 +0530 Subject: [PATCH 15/81] restrict changing brave mode when proxy is selected --- .../HomeScreenSettingBottomSheet.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/HomeScreenSettingBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/HomeScreenSettingBottomSheet.kt index 22bc8620a..40b4109fb 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/HomeScreenSettingBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/HomeScreenSettingBottomSheet.kt @@ -157,19 +157,33 @@ class HomeScreenSettingBottomSheet : BottomSheetDialogFragment() { // disable dns and firewall mode, show user that vpn in lockdown mode indicator if needed private fun handleLockdownModeIfNeeded() { val isLockdown = VpnController.isVpnLockdown() + val isProxyEnabled = appConfig.isProxyEnabled() if (isLockdown) { b.bsHomeScreenVpnLockdownDesc.visibility = View.VISIBLE b.bsHsDnsRl.alpha = 0.5f b.bsHsFirewallRl.alpha = 0.5f + setRadioButtonsEnabled(false) + } else if (isProxyEnabled) { + b.bsHomeScreenVpnLockdownDesc.text = getString(R.string.settings_lock_down_proxy_desc) + b.bsHomeScreenVpnLockdownDesc.visibility = View.VISIBLE + b.bsHsDnsRl.alpha = 0.5f + b.bsHsFirewallRl.alpha = 0.5f + setRadioButtonsEnabled(false) } else { b.bsHomeScreenVpnLockdownDesc.visibility = View.GONE b.bsHsDnsRl.alpha = 1f b.bsHsFirewallRl.alpha = 1f + setRadioButtonsEnabled(true) } - b.bsHsDnsRl.isEnabled = !isLockdown - b.bsHsFirewallRl.isEnabled = !isLockdown - b.bsHomeScreenRadioFirewall.isEnabled = !isLockdown - b.bsHomeScreenRadioDns.isEnabled = !isLockdown + } + + private fun setRadioButtonsEnabled(isEnabled: Boolean) { + b.bsHsDnsRl.isEnabled = isEnabled + b.bsHsFirewallRl.isEnabled = isEnabled + b.bsHsDnsFirewallRl.isEnabled = isEnabled + b.bsHomeScreenRadioDns.isEnabled = isEnabled + b.bsHomeScreenRadioFirewall.isEnabled = isEnabled + b.bsHomeScreenRadioDnsFirewall.isEnabled = isEnabled } private fun handleDnsMode(isChecked: Boolean) { From 86b649e15a335f062d6d5526df819cb555befc36 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 21:12:10 +0530 Subject: [PATCH 16/81] setting to skip proxying DNS requests to proxies --- .../ui/fragment/DnsSettingsFragment.kt | 28 ++++----- .../bravedns/service/PersistentState.kt | 3 + .../res/layout/fragment_dns_configure.xml | 57 +++++++++++++++++++ 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt index 8d46fa64c..6bc8a69dc 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt @@ -103,6 +103,8 @@ class DnsSettingsFragment : b.dcAlgSwitch.isChecked = persistentState.enableDnsAlg // enable dns caching in tunnel b.dcEnableCacheSwitch.isChecked = persistentState.enableDnsCache + // proxy dns + b.dcProxyDnsSwitch.isChecked = !persistentState.proxyDns b.connectedStatusTitle.text = getConnectedDnsType() } @@ -149,10 +151,9 @@ class DnsSettingsFragment : } private fun updateConnectedStatus(connectedDns: String) { - val oneWg = oneWireGuard() - if (oneWg) { - b.connectedStatusTitleUrl.text = resources.getString(R.string.lbl_wireguard) - b.connectedStatusTitle.text = "" + if (WireguardManager.oneWireGuardEnabled()) { + b.connectedStatusTitleUrl.text = resources.getString(R.string.configure_dns_connected_dns_proxy_status) + b.connectedStatusTitle.text = resources.getString(R.string.lbl_wireguard) disableAllDns() b.wireguardRb.isEnabled = true return @@ -204,8 +205,7 @@ class DnsSettingsFragment : } private fun updateSelectedDns() { - val oneWg = oneWireGuard() - if (oneWg) { + if (WireguardManager.oneWireGuardEnabled()) { b.wireguardRb.visibility = View.VISIBLE b.wireguardRb.isChecked = true b.wireguardRb.isEnabled = true @@ -241,10 +241,6 @@ class DnsSettingsFragment : b.networkDnsRb.isClickable = false } - private fun oneWireGuard(): Boolean { - return WireguardManager.oneWireGuardEnabled() - } - private fun getConnectedDnsType(): String { return when (appConfig.getDnsType()) { AppConfig.DnsType.RETHINK_REMOTE -> { @@ -356,6 +352,14 @@ class DnsSettingsFragment : persistentState.enableDnsCache = b } + b.dcProxyDnsSwitch.setOnCheckedChangeListener { _, b -> + persistentState.proxyDns = !b + } + + b.dcProxyDnsRl.setOnClickListener { + b.dcProxyDnsSwitch.isChecked = !b.dcProxyDnsSwitch.isChecked + } + b.dcRefresh.setOnClickListener { b.dcRefresh.isEnabled = false b.dcRefresh.animation = animation @@ -427,10 +431,6 @@ class DnsSettingsFragment : lifecycleScope.launch(Dispatchers.IO) { f() } } - private suspend fun uiCtx(f: suspend () -> Unit) { - withContext(Dispatchers.Main) { f() } - } - override fun onBtmSheetDismiss() { if (!isAdded) return diff --git a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt index fb80f9ea8..a3a1fb425 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt @@ -271,6 +271,9 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { // route rethink in rethink var routeRethinkInRethink by booleanPref("route_rethink_in_rethink").withDefault(false) + // proxy dns requests over proxy + var proxyDns by booleanPref("proxy_dns").withDefault(true) + var orbotConnectionStatus: MutableLiveData = MutableLiveData() var median: MutableLiveData = MutableLiveData() var vpnEnabledLiveData: MutableLiveData = MutableLiveData() diff --git a/app/src/main/res/layout/fragment_dns_configure.xml b/app/src/main/res/layout/fragment_dns_configure.xml index 9c54d04d3..5eabd011f 100644 --- a/app/src/main/res/layout/fragment_dns_configure.xml +++ b/app/src/main/res/layout/fragment_dns_configure.xml @@ -584,6 +584,63 @@ + + + + + + + + + + + + + + + Date: Tue, 19 Mar 2024 21:13:23 +0530 Subject: [PATCH 17/81] fix: correctly display DNS status on home screen card --- .../ui/fragment/HomeScreenFragment.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index 0bd1f8bac..5996597b9 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -392,7 +392,7 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { val status = VpnController.getProxyStatusById(proxyId) if (status != null) { // consider starting and up as active - if (status == Backend.TOK || status == Backend.TUP) { + if (status == Backend.TOK || status == Backend.TUP || status == Backend.TZZ) { active++ } else { failing++ @@ -512,14 +512,16 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { } private fun updateUiWithDnsStates(dnsName: String) { + var dns = dnsName val preferredId = if (appConfig.isSystemDns()) Backend.System else Backend.Preferred // get the status from go to check if the dns transport is added or not val id = - if (WireguardManager.oneWireGuardEnabled() && persistentState.proxyDns) { + if (WireguardManager.oneWireGuardEnabled()) { val id = WireguardManager.getOneWireGuardProxyId() if (id == null) { preferredId } else { + dns = getString(R.string.lbl_wireguard) "${ProxyManager.ID_WG_BASE}${id}" } } else { @@ -528,24 +530,29 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { if (VpnController.isOn()) { ui("dnsStatusCheck") { + var failing = false repeat(5) { val status = VpnController.getDnsStatus(id) if (status != null) { - if (status == Backend.TOK) { + failing = false + if (isAdded) { b.fhsCardDnsLatency.visibility = View.VISIBLE b.fhsCardDnsFailure.visibility = View.GONE - return@ui } + return@ui } - // status null means the dns transport is not available / different id is used + // status null means the dns transport is not active / different id is used kotlinx.coroutines.delay(1000L) + failing = true + } + if (failing && isAdded) { + b.fhsCardDnsLatency.visibility = View.GONE + b.fhsCardDnsFailure.visibility = View.VISIBLE + b.fhsCardDnsFailure.text = getString(R.string.failed_using_default) } - b.fhsCardDnsLatency.visibility = View.GONE - b.fhsCardDnsFailure.visibility = View.VISIBLE - b.fhsCardDnsFailure.text = getString(R.string.failed_using_default) } } - b.fhsCardDnsConnectedDns.text = dnsName + b.fhsCardDnsConnectedDns.text = dns b.fhsCardDnsConnectedDns.isSelected = true } From f150b9b0ee2a9081ecec1cb836f9f70107354f7b Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 21:15:05 +0530 Subject: [PATCH 18/81] refactor: enhance connection monitor functionality --- .../bravedns/service/ConnectionMonitor.kt | 211 +++++++----------- 1 file changed, 83 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt index 027fcce39..df3ff088e 100644 --- a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt +++ b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt @@ -29,7 +29,6 @@ import android.os.Message import android.os.SystemClock import android.system.ErrnoException import android.system.OsConstants.ECONNREFUSED -import android.system.OsConstants.RT_SCOPE_UNIVERSE import android.util.Log import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.util.InternetProtocol @@ -38,22 +37,24 @@ import com.celzero.bravedns.util.Utilities.isAtleastQ import com.celzero.bravedns.util.Utilities.isAtleastS import com.celzero.bravedns.util.Utilities.isNetworkSame import com.google.common.collect.Sets -import inet.ipaddr.IPAddressString -import kotlinx.coroutines.async -import kotlinx.coroutines.cancelChildren -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.selects.select -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.io.Closeable import java.io.IOException import java.net.DatagramSocket +import java.net.Inet4Address +import java.net.Inet6Address import java.net.InetAddress import java.net.InetSocketAddress import java.net.Socket import java.util.concurrent.TimeUnit +import kotlin.math.max import kotlin.math.min +import kotlinx.coroutines.async +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.selects.select +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject class ConnectionMonitor(context: Context, networkListener: NetworkListener) : ConnectivityManager.NetworkCallback(), KoinComponent { @@ -161,7 +162,6 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : } override fun onAvailable(network: Network) { - if (DEBUG) Log.d(LOG_TAG_CONNECTION, "onAvailable: ${network.networkHandle}, $network") networkSet.add(network) val cap = connectivityManager.getNetworkCapabilities(network) if (DEBUG) @@ -173,7 +173,6 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : } override fun onLost(network: Network) { - if (DEBUG) Log.d(LOG_TAG_CONNECTION, "onLost, ${network.networkHandle}, $network") networkSet.remove(network) val cap = connectivityManager.getNetworkCapabilities(network) if (DEBUG) @@ -263,8 +262,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : val ipv6Net: List, val useActive: Boolean, val minMtu: Int, - var isActiveNetworkMetered: Boolean, - var lastUpdated: Long + var isActiveNetworkMetered: Boolean, // may be updated by client listener + var lastUpdated: Long // may be updated by client listener ) // Handles the network messages from the callback from the connectivity manager @@ -278,8 +277,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : private val maxReachabilityCount = 10L companion object { - private const val MAX_INTERFACE_MTU = 1500 // same as BraveVpnService#VPN_INTERFACE_MTU - private const val MIN_INTERFACE_MTU = 1280 + private const val DEFAULT_MTU = 1500 // same as BraveVpnService#VPN_INTERFACE_MTU + private const val MIN_MTU = 1280 } private val ip4probes = @@ -374,6 +373,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : useActiveNetwork: Boolean = false, isActiveNetworkMetered: Boolean ) { + // TODO: use currentNetworks instead of trackedIpv4Networks and trackedIpv6Networks + // to determine whether to call onNetworkConnected or onNetworkDisconnected val sz = trackedIpv4Networks.size + trackedIpv6Networks.size Log.i( LOG_TAG_CONNECTION, @@ -404,7 +405,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : emptyList(), emptyList(), useActiveNetwork, - MAX_INTERFACE_MTU, + DEFAULT_MTU, isActiveNetworkMetered = false, SystemClock.elapsedRealtime() ) @@ -413,76 +414,49 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : } private fun determineMtu(useActiveNetwork: Boolean): Int { + var minMtu4: Int = DEFAULT_MTU + var minMtu6: Int = DEFAULT_MTU if (!isAtleastQ()) { - return MAX_INTERFACE_MTU + return minMtu4 } - - var mtu = - if (useActiveNetwork) { - val activeNetwork = connectivityManager.activeNetwork - // consider first network in underlying network as active network, in case - // if active network is null - val lp4 = - trackedIpv4Networks.firstOrNull()?.linkProperties - ?: connectivityManager.getLinkProperties(activeNetwork) - val lp6 = - trackedIpv6Networks.firstOrNull()?.linkProperties - ?: connectivityManager.getLinkProperties(activeNetwork) - minNonZeroMtu(lp4?.mtu, lp6?.mtu) - } else { - var prev4Lp: LinkProperties? = null - var prev6Lp: LinkProperties? = null - var minMtu4: Int = MAX_INTERFACE_MTU - var minMtu6: Int = MAX_INTERFACE_MTU - // parse through all the networks and get the minimum mtu - trackedIpv4Networks.forEach { - if (DEBUG) - Log.d(LOG_TAG_CONNECTION, "mtu for ipv4: ${it.linkProperties?.mtu}") - val c = it.linkProperties - minMtu4 = minNonZeroMtu(c?.mtu, prev4Lp?.mtu) ?: minMtu4 - prev4Lp = c - } - trackedIpv6Networks.forEach { - if (DEBUG) - Log.d(LOG_TAG_CONNECTION, "mtu for ipv6: ${it.linkProperties?.mtu}") - val c = it.linkProperties - minMtu6 = minNonZeroMtu(c?.mtu, prev6Lp?.mtu) ?: minMtu6 - prev6Lp = c + if (useActiveNetwork) { + connectivityManager.activeNetwork?.let { + val lp = connectivityManager.getLinkProperties(it) + minMtu4 = minNonZeroMtu(lp?.mtu, minMtu4) + minMtu6 = minMtu4 // assume same mtu for both ipv4 and ipv6 + } + ?: { + // consider first network in underlying network as active network, + // in case active network is null + val lp4 = trackedIpv4Networks.firstOrNull()?.linkProperties + val lp6 = trackedIpv6Networks.firstOrNull()?.linkProperties + minMtu4 = minNonZeroMtu(lp4?.mtu, minMtu4) + minMtu6 = minNonZeroMtu(lp6?.mtu, minMtu6) } - if (DEBUG) - Log.d(LOG_TAG_CONNECTION, "min mtu for v4 nws: $minMtu4, v6 nws: $minMtu6") - min(minMtu4, minMtu6) + } else { + // parse through all the networks and get the minimum mtu + trackedIpv4Networks.forEach { + val c = it.linkProperties + minMtu4 = minNonZeroMtu(c?.mtu, minMtu4) + } + trackedIpv6Networks.forEach { + val c = it.linkProperties + minMtu6 = minNonZeroMtu(c?.mtu, minMtu6) } - - // mtu can be 0 or null, set it to default value - // LinkProperties#getMtu() returns 0 if the value is not set - // minNonZeroMtu can returns null - if (mtu == null || mtu == 0) { - mtu = MAX_INTERFACE_MTU - Log.i(LOG_TAG_CONNECTION, "mtu is 0, setting to default $MAX_INTERFACE_MTU") - } - - // min value is 1280, set it to 1280 if it is less than 1280 - if (mtu < MIN_INTERFACE_MTU) { - mtu = MIN_INTERFACE_MTU - Log.i(LOG_TAG_CONNECTION, "mtu is less than 1280, setting to 1280") } - - Log.i(LOG_TAG_CONNECTION, "current mtu: $mtu") + // set mtu to MIN_MTU (1280) if mtu4/mtu6 are less than MIN_MTU + val mtu = max(min(minMtu4, minMtu6), MIN_MTU) + Log.i(LOG_TAG_CONNECTION, "mtu4: $minMtu4, mtu6: $minMtu6; final mtu: $mtu") return mtu } - private fun minNonZeroMtu(m1: Int?, m2: Int?): Int? { - return if (m1 != null && m1 > 0 && m2 != null && m2 > 0) { + private fun minNonZeroMtu(m1: Int?, m2: Int): Int { + return if (m1 != null && m1 > 0) { // mtu can be null when lp is null // mtu can be 0 when the value is not set, see:LinkProperties#getMtu() min(m1, m2) - } else if (m1 != null && m1 > 0) { - m1 - } else if (m2 != null && m2 > 0) { - m2 } else { - null + m2 } } @@ -516,17 +490,14 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : if (DEBUG) Log.d(LOG_TAG_CONNECTION, "processing active network: $network") } - var has4 = false - var has6 = false - if (testReachability) { // for active network, ICMP echo is additionally used with TCP and UDP checks // but ICMP echo will always return reachable when app is in rinr mode // so till we have checks for rinr mode, we should not use ICMP reachability val canUseIcmp = false // for now, need to check for rinr mode val useIcmp = isActive && canUseIcmp - has4 = probeConnectivity(ip4probes, network, useIcmp) - has6 = probeConnectivity(ip6probes, network, useIcmp) + val has4 = probeConnectivity(ip4probes, network, useIcmp) + val has6 = probeConnectivity(ip6probes, network, useIcmp) if (has4) trackedIpv4Networks.add(prop) if (has6) trackedIpv6Networks.add(prop) Log.i(LOG_TAG_CONNECTION, "nw: has4? $has4, has6? $has6, $prop") @@ -534,42 +505,35 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : // else: fall-through to check reachability with network capabilities } - lp.linkAddresses.forEach inner@{ addr -> - if (has4 && has6) return@outer - // skip if the address scope is not RT_SCOPE_UNIVERSE, as there are some - // addresses which are not reachable outside the network but we will end up - // adding those address for ipv4/ipv6 mode, reachability check will fail in - // dual stack mode. - if (!dualStack && addr.scope != RT_SCOPE_UNIVERSE) { - Log.i( - LOG_TAG_CONNECTION, - "skipping: ${addr.address.hostAddress} with scope: ${addr.scope}" - ) - return@inner + // see #createNetworksSet for why we are using hasInternet + if (hasInternet(network) == true) { + var hasDefaultRoute4 = false + var hasDefaultRoute6 = false + lp.routes.forEach rloop@{ + // ref: androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/net/RouteInfo.java#328 + hasDefaultRoute4 = + hasDefaultRoute4 || + (it.isDefaultRoute && it.destination.address is Inet4Address) + hasDefaultRoute6 = + hasDefaultRoute6 || + (it.isDefaultRoute && it.destination.address is Inet6Address) + + if (hasDefaultRoute4 && hasDefaultRoute6) return@rloop } - val address = IPAddressString(addr.address.hostAddress?.toString()) - - if (hasInternet(network) == true) { - if (address.isIPv6) { - has6 = true - trackedIpv6Networks.add(prop) - Log.i( - LOG_TAG_CONNECTION, - "adding ipv6(${trackedIpv6Networks.size}): $prop; for $address" - ) - } else if (address.isIPv4) { - has4 = true - // see #createNetworksSet for why we are using hasInternet - trackedIpv4Networks.add(prop) - Log.i( - LOG_TAG_CONNECTION, - "adding ipv4(${trackedIpv4Networks.size}): $prop; for $address" - ) - } else { - Log.i(LOG_TAG_CONNECTION, "unknown lnaddr: $network; $address") - } + if (hasDefaultRoute6) { + trackedIpv6Networks.add(prop) } + if (hasDefaultRoute4) { + trackedIpv4Networks.add(prop) + } + + Log.i( + LOG_TAG_CONNECTION, + "nw: default4? $hasDefaultRoute4, default6? $hasDefaultRoute6 for $prop" + ) + } else { + Log.i(LOG_TAG_CONNECTION, "skip: $network; no internet capability") } } @@ -624,18 +588,13 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : if (n != null) { newNetworks.add(n) } - val nonMeteredNetworks = networks.filter { isConnectionNotMetered(it.capabilities) } - nonMeteredNetworks.forEach { - if (!newNetworks.contains(it)) { - newNetworks.add(it) - } - } + networks + .filter { isConnectionNotMetered(it.capabilities) } + .forEach { newNetworks.add(it) } // add remaining networks, ie, metered networks - networks.forEach { - if (!newNetworks.contains(it)) { - newNetworks.add(it) - } - } + networks + .filter { !isConnectionNotMetered(it.capabilities) } + .forEach { newNetworks.add(it) } return newNetworks } @@ -679,11 +638,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : if (DEBUG) Log.d(LOG_TAG_CONNECTION, "networkSet is empty") connectivityManager.allNetworks } else { - if (DEBUG) - Log.d( - LOG_TAG_CONNECTION, - "networkSet is not empty, size: ${networkSet.size}" - ) + if (DEBUG) Log.d(LOG_TAG_CONNECTION, "networkSet size: ${networkSet.size}") networkSet.toTypedArray() } From 3ac71f36e097115d6d34e2bbcbd9b1ddf84063d9 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 21:18:51 +0530 Subject: [PATCH 19/81] feat: icon for skip proxying DNS setting --- .../res/drawable/ic_prevent_dns_proxy.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/src/main/res/drawable/ic_prevent_dns_proxy.xml diff --git a/app/src/main/res/drawable/ic_prevent_dns_proxy.xml b/app/src/main/res/drawable/ic_prevent_dns_proxy.xml new file mode 100644 index 000000000..c7ea6e9b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_prevent_dns_proxy.xml @@ -0,0 +1,21 @@ + + + + From 88aba12ada9bca1fab40286b9ed5615e8cd4ea80 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 19 Mar 2024 21:50:48 +0530 Subject: [PATCH 20/81] minor wireguard ui changes & handling of states --- .../bravedns/adapter/OneWgConfigAdapter.kt | 49 ++++---- .../bravedns/service/WireguardManager.kt | 106 ++++++++++-------- .../ui/activity/ProxySettingsActivity.kt | 2 +- .../bravedns/ui/activity/WgMainActivity.kt | 10 +- .../java/com/celzero/bravedns/util/UIUtils.kt | 3 + .../bravedns/database/WgConfigFilesDAO.kt | 4 +- .../res/layout/activity_wg_config_editor.xml | 7 +- .../main/res/layout/activity_wg_detail.xml | 2 - 8 files changed, 101 insertions(+), 82 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt index 75a85dd98..e409b4488 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt @@ -22,6 +22,7 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -45,10 +46,16 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class OneWgConfigAdapter(private val context: Context, private val lifecycleOwner: LifecycleOwner) : +class OneWgConfigAdapter(private val context: Context, private val listener: DnsStatusListener) : PagingDataAdapter(DIFF_CALLBACK) { - private var statusCheckJob: Job = Job() + private var statusCheckJob: Job? = Job() + private var lifecycleOwner: LifecycleOwner? = null + + interface DnsStatusListener { + fun onDnsStatusChanged() + } + companion object { private const val DELAY = 1000L @@ -86,6 +93,7 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne parent, false ) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return WgInterfaceViewHolder(itemBinding) } @@ -100,7 +108,7 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne if (config.oneWireGuard) { keepStatusUpdated(config) } else { - statusCheckJob.cancel() + statusCheckJob?.cancel() b.interfaceDetailCard.strokeWidth = 0 b.interfaceAppsCount.visibility = View.GONE b.oneWgCheck.isChecked = false @@ -119,16 +127,6 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne } private fun updateStatus(config: WgConfigFiles) { - val id = ProxyManager.ID_WG_BASE + config.id - val apps = ProxyManager.getAppCountForProxy(id).toString() - val statusId = VpnController.getProxyStatusById(id) - if (statusId == null && config.isActive) { - WireguardManager.disableConfig(config) - } - updateStatusUi(config, statusId, apps) - } - - private fun handleSwitchClick(config: WgConfigFiles) { val id = ProxyManager.ID_WG_BASE + config.id val apps = ProxyManager.getAppCountForProxy(id).toString() val statusId = VpnController.getProxyStatusById(id) @@ -138,11 +136,13 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne private fun updateStatusUi(config: WgConfigFiles, statusId: Long?, apps: String) { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner != null && + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -160,7 +160,7 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextPositive) // cancel the job, as the status is connected - statusCheckJob.cancel() + statusCheckJob?.cancel() } else if (statusId == Backend.TUP || statusId == Backend.TZZ) { b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextNeutral) @@ -200,7 +200,7 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne config.oneWireGuard = true WireguardManager.updateOneWireGuardConfig(config.id, owg = true) WireguardManager.enableConfig(config) - uiCtx { handleSwitchClick(config) } + uiCtx { listener.onDnsStatusChanged() } } else { uiCtx { b.oneWgCheck.isChecked = false @@ -216,7 +216,7 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne b.oneWgCheck.isChecked = false WireguardManager.updateOneWireGuardConfig(config.id, owg = false) WireguardManager.disableConfig(config) - uiCtx { handleSwitchClick(config) } + uiCtx { listener.onDnsStatusChanged() } } } } @@ -234,11 +234,14 @@ class OneWgConfigAdapter(private val context: Context, private val lifecycleOwne withContext(Dispatchers.Main) { f() } } - private fun ui(f: suspend () -> Unit): Job { - return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + private fun ui(f: suspend () -> Unit): Job? { + if (lifecycleOwner == null) { + return null + } + return lifecycleOwner?.lifecycleScope?.launch(Dispatchers.Main) { f() } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } + lifecycleOwner?.lifecycleScope?.launch(Dispatchers.IO) { f() } } } diff --git a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt index 9aff83829..7276bb77a 100644 --- a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt @@ -26,6 +26,7 @@ import com.celzero.bravedns.data.AppConfig import com.celzero.bravedns.database.WgConfigFiles import com.celzero.bravedns.database.WgConfigFilesRepository import com.celzero.bravedns.util.Constants.Companion.WIREGUARD_FOLDER_NAME +import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_PROXY import com.celzero.bravedns.wireguard.BadConfigException import com.celzero.bravedns.wireguard.Config @@ -103,13 +104,10 @@ object WireguardManager : KoinComponent { } fun getConfigById(id: Int): Config? { - Log.d(LOG_TAG_PROXY, "after with lock getConfigById: ${configs.size}") val config = configs.find { it.getId() == id } - Log.d(LOG_TAG_PROXY, "after find getConfigById: ${configs.size}") if (config == null) { Log.e(LOG_TAG_PROXY, "getConfigById: wg not found: $id, ${configs.size}") } - Log.d(LOG_TAG_PROXY, "getConfigById: $id, ${config?.getName()}") return config } @@ -122,8 +120,7 @@ object WireguardManager : KoinComponent { } fun isAnyWgActive(): Boolean { - val m = mappings.filter { it.isActive } - return m.isNotEmpty() + return mappings.any { it.isActive } } fun getEnabledConfigs(): List { @@ -169,7 +166,13 @@ object WireguardManager : KoinComponent { return configs.any { it.getId() == SEC_WARP_ID } } - fun enableConfig(map: WgConfigFiles) { + fun enableConfig(unmapped: WgConfigFiles) { + val map = mappings.find { it.id == unmapped.id } + if (map == null) { + Log.e(LOG_TAG_PROXY, "enableConfig: wg not found, id: ${unmapped.id}, ${mappings.size}") + return + } + val config = configs.find { it.getId() == map.id } // no need to enable config if it is sec warp if (config == null || config.getId() == SEC_WARP_ID) { @@ -178,20 +181,12 @@ object WireguardManager : KoinComponent { } // enable the config, update to db, cache and tunnel - map.isActive = true + map.isActive = true // also update mappings: https://pl.kotl.in/g0mVapn4x io { db.update(map) } - mappings.find { it.id == map.id }?.isActive = true - val proxyType = AppConfig.ProxyType.WIREGUARD val proxyProvider = AppConfig.ProxyProvider.WIREGUARD appConfig.addProxy(proxyType, proxyProvider) - // in case of multiple wg configs, first proxy will be added with appConfig proxy pref - // value, for the rest instead of calling updateTun, directly add the proxy to the - // tunnel - if (mappings.filter { it.isActive }.size > 1) { - Log.w(LOG_TAG_PROXY, "More than one wg config is active") - VpnController.addWireGuardProxy(ProxyManager.ID_WG_BASE + map.id) - } + VpnController.addWireGuardProxy(ProxyManager.ID_WG_BASE + map.id) Log.i(LOG_TAG_PROXY, "enable wg config: ${map.id}, ${map.name}") return } @@ -257,7 +252,13 @@ object WireguardManager : KoinComponent { } } - fun disableConfig(map: WgConfigFiles) { + fun disableConfig(unmapped: WgConfigFiles) { + val map = mappings.find { it.id == unmapped.id } + if (map == null) { + Log.e(LOG_TAG_PROXY, "disableConfig: wg not found, id: ${unmapped.id}, ${mappings.size}") + return + } + val config = configs.find { it.getId() == map.id } // no need to enable config if it is sec warp if (config == null || config.getId() == SEC_WARP_ID) { @@ -266,9 +267,10 @@ object WireguardManager : KoinComponent { } // disable the config, update to db, cache and tunnel - map.isActive = false + // also update mappings https://pl.kotl.in/g0mVapn4x + map.isActive = false // confirms with db.disableConfig query + map.oneWireGuard = false // confirms with db.disableConfig query io { db.disableConfig(map.id) } - mappings.find { it.id == map.id }?.isActive = false if (mappings.none { it.isActive }) { val proxyType = AppConfig.ProxyType.WIREGUARD val proxyProvider = AppConfig.ProxyProvider.WIREGUARD @@ -280,7 +282,7 @@ object WireguardManager : KoinComponent { return } - suspend fun getNewWarpConfig(id: Int): Config? { + suspend fun getNewWarpConfig(id: Int, retryCount: Int = 0): Config? { try { val privateKey = Backend.newWgPrivateKey() val publicKey = privateKey.mult().base64() @@ -288,7 +290,7 @@ object WireguardManager : KoinComponent { val locale = Locale.getDefault().toString() val retrofit = - RetrofitManager.getWarpBaseBuilder() + RetrofitManager.getWarpBaseBuilder(retryCount) .addConverterFactory(GsonConverterFactory.create()) .build() val retrofitInterface = retrofit.create(IWireguardWarp::class.java) @@ -296,7 +298,7 @@ object WireguardManager : KoinComponent { val response = retrofitInterface.getNewWarpConfig(publicKey, deviceName, locale) if (DEBUG) Log.d(LOG_TAG_PROXY, "New wg(warp) config: ${response?.body()}") - return if (response?.isSuccessful == true) { + if (response?.isSuccessful == true) { val jsonObject = JSONObject(response.body().toString()) val config = parseNewConfigJsonResponse(privateKey, jsonObject) if (config != null) { @@ -309,26 +311,30 @@ object WireguardManager : KoinComponent { writeConfigAndUpdateDb(config, jsonObject.toString()) } - config - } else { - Log.w( - LOG_TAG_PROXY, - "err: new wg(warp) config: ${response?.message()}, ${response?.errorBody()}, ${response?.code()}" - ) - null + return config } } catch (e: Exception) { Log.e(LOG_TAG_PROXY, "err: new wg(warp) config: ${e.message}") - return null + } + return if (isRetryRequired(retryCount)) { + Log.i(Logger.LOG_TAG_DOWNLOAD, "retrying to getNewWarpConfig") + getNewWarpConfig(id, retryCount + 1) + } else { + Log.i(LOG_TAG_PROXY, "retry count exceeded(getNewWarpConfig), returning null") + null } } - suspend fun isWarpWorking(): Boolean { + private fun isRetryRequired(retryCount: Int): Boolean { + return retryCount < RetrofitManager.Companion.OkHttpDnsType.entries.size - 1 + } + + suspend fun isWarpWorking(retryCount: Int = 0): Boolean { // create okhttp client with base url var works = false try { val retrofit = - RetrofitManager.getWarpBaseBuilder() + RetrofitManager.getWarpBaseBuilder(retryCount) .addConverterFactory(GsonConverterFactory.create()) .build() val retrofitInterface = retrofit.create(IWireguardWarp::class.java) @@ -355,7 +361,13 @@ object WireguardManager : KoinComponent { Log.e(LOG_TAG_PROXY, "err checking warp(works): ${e.message}") } - return works + return if (isRetryRequired(retryCount) && !works) { + Log.i(Logger.LOG_TAG_DOWNLOAD, "retrying to getNewWarpConfig") + isWarpWorking(retryCount + 1) + } else { + Log.i(LOG_TAG_PROXY, "retry count exceeded(getNewWarpConfig), returning null") + works + } } fun getConfigIdForApp(uid: Int): WgConfigFiles? { @@ -373,7 +385,7 @@ object WireguardManager : KoinComponent { } val id = convertStringIdToId(configId) - return mappings.find { it.id == id } ?: return null + return mappings.find { it.id == id } } private fun convertStringIdToId(id: String): Int { @@ -478,7 +490,9 @@ object WireguardManager : KoinComponent { .build() Log.i(LOG_TAG_PROXY, "updating interface for config: $configId, ${config.getName()}") val cfgId = ProxyManager.ID_WG_BASE + configId - ProxyManager.setProxyIdForAllApps(cfgId, configName) + if (configName != config.getName()) { + ProxyManager.updateProxyNameForProxyId(cfgId, configName) + } writeConfigAndUpdateDb(cfg) return cfg } @@ -537,10 +551,9 @@ object WireguardManager : KoinComponent { } Log.i(LOG_TAG_PROXY, "updating lockdown for config: $id, ${config.getName()}") db.updateLockdownConfig(id, isLockdown) - mappings.find { it.id == id }?.isLockdown = isLockdown + map?.isLockdown = isLockdown if (map?.isActive == true) { - // updates the vpn if the config is active - VpnController.updateWireGuardConfig() + VpnController.addWireGuardProxy(id = ProxyManager.ID_WG_BASE + config.getId()) } } @@ -553,9 +566,9 @@ object WireguardManager : KoinComponent { Log.i(LOG_TAG_PROXY, "updating catch all for config: $id, ${config.getName()}") db.updateCatchAllConfig(id, isEnabled) val map = mappings.find { it.id == id } ?: return - map.isCatchAll = isEnabled - // catch all should be always enabled - enableConfig(map) + map.isCatchAll = isEnabled // confirms with db.updateCatchAllConfig query + map.oneWireGuard = false // confirms with db.updateCatchAllConfig query + enableConfig(map) // catch all should be always enabled } suspend fun updateOneWireGuardConfig(id: Int, owg: Boolean) { @@ -567,10 +580,9 @@ object WireguardManager : KoinComponent { } Log.i(LOG_TAG_PROXY, "update one wg, id: $id, ${config.getName()} to $owg") db.updateOneWireGuardConfig(id, owg) - mappings.find { it.id == id }?.oneWireGuard = owg + map?.oneWireGuard = owg if (map?.isActive == true) { - // updates the vpn if the config is active - VpnController.updateWireGuardConfig() + VpnController.addWireGuardProxy(id = ProxyManager.ID_WG_BASE + config.getId()) } } @@ -666,8 +678,7 @@ object WireguardManager : KoinComponent { addOrUpdateConfigFileMapping(cfg, file, path, serverResponse) addOrUpdateConfig(cfg) if (file?.isActive == true) { - // updates the vpn if the config is active - VpnController.updateWireGuardConfig() + VpnController.addWireGuardProxy(id = ProxyManager.ID_WG_BASE + cfg.getId()) } } @@ -726,6 +737,7 @@ object WireguardManager : KoinComponent { ProxyManager.removeWgProxies() Log.i(LOG_TAG_PROXY, "Deleted wg entries: $count") clearLoadedConfigs() + load() } } @@ -742,7 +754,7 @@ object WireguardManager : KoinComponent { } fun getCatchAllWireGuardProxyId(): Int? { - return mappings.find { it.isCatchAll && it.isActive }?.id ?: return null + return mappings.find { it.isCatchAll && it.isActive }?.id } fun canRouteIp(configId: Int?, ip: String?): Boolean { diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt index a2c385e54..ef34938d7 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt @@ -488,7 +488,7 @@ class ProxySettingsActivity : AppCompatActivity(R.layout.fragment_proxy_configur getString( R.string.ci_ip_label, it.getName(), - getString(R.string.status_failing).padStart(1, ' ') + getString(R.string.status_waiting).padStart(1, ' ') ) .replaceFirstChar(Char::titlecase) + "\n" if (DEBUG) Log.d(LOG_TAG_PROXY, "current proxy status is null for $id") diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt index c1fd9bd58..3de523492 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt @@ -55,7 +55,7 @@ import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { +class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main), OneWgConfigAdapter.DnsStatusListener { private val b by viewBinding(ActivityWireguardMainBinding::bind) private val persistentState by inject() private val appConfig by inject() @@ -64,6 +64,7 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { private var oneWgConfigAdapter: OneWgConfigAdapter? = null private val wgConfigViewModel: WgConfigViewModel by viewModel() + companion object { private const val IMPORT_LAUNCH_INPUT = "*/*" } @@ -282,6 +283,8 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { val dnsName = activeConfigs.first().getName() b.wgWireguardDisclaimer.text = getString(R.string.wireguard_disclaimer, dnsName) } + // remove the observer if any config is active + appConfig.getConnectedDnsObservable().removeObservers(this) } else { appConfig.getConnectedDnsObservable().observe(this) { b.wgWireguardDisclaimer.text = getString(R.string.wireguard_disclaimer, it) @@ -345,6 +348,7 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { if (WireguardManager.canDisableAllActiveConfigs()) { WireguardManager.disableAllActiveConfigs() uiCtx { + this.observeDnsName() if (isOneWgToggle) { showOneWgToggle() } else { @@ -394,4 +398,8 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main) { private suspend fun uiCtx(f: suspend () -> Unit) { withContext(Dispatchers.Main) { f() } } + + override fun onDnsStatusChanged() { + observeDnsName() + } } diff --git a/app/src/full/java/com/celzero/bravedns/util/UIUtils.kt b/app/src/full/java/com/celzero/bravedns/util/UIUtils.kt index 16be8db8d..f4e985152 100644 --- a/app/src/full/java/com/celzero/bravedns/util/UIUtils.kt +++ b/app/src/full/java/com/celzero/bravedns/util/UIUtils.kt @@ -89,6 +89,9 @@ object UIUtils { Backend.TOK -> { R.string.dns_connected } + Backend.TZZ -> { + R.string.lbl_idle + } Backend.TKO -> { R.string.status_failing } diff --git a/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt b/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt index fba6af0b3..baaec1eac 100644 --- a/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt @@ -55,7 +55,7 @@ interface WgConfigFilesDAO { @Query("delete from WgConfigFiles where id = :id") fun deleteConfig(id: Int) - @Query("update WgConfigFiles set isCatchAll = :isCatchAll where id = :id") + @Query("update WgConfigFiles set isCatchAll = :isCatchAll, oneWireGuard = 0 where id = :id") fun updateCatchAllConfig(id: Int, isCatchAll: Boolean) @Query("update WgConfigFiles set oneWireGuard = :oneWireGuard where id = :id") @@ -69,5 +69,5 @@ interface WgConfigFilesDAO { @Query("select count(id) from WgConfigFiles where id != $SEC_WARP_ID and id != $WARP_ID") fun getConfigCount(): LiveData - @Query("update WgConfigFiles set isActive = 0 where id = :id") fun disableConfig(id: Int) + @Query("update WgConfigFiles set isActive = 0, oneWireGuard = 0 where id = :id") fun disableConfig(id: Int) } diff --git a/app/src/main/res/layout/activity_wg_config_editor.xml b/app/src/main/res/layout/activity_wg_config_editor.xml index ede8ddedd..fc971eb34 100644 --- a/app/src/main/res/layout/activity_wg_config_editor.xml +++ b/app/src/main/res/layout/activity_wg_config_editor.xml @@ -239,17 +239,12 @@ + android:textAlignment="center" /> diff --git a/app/src/main/res/layout/activity_wg_detail.xml b/app/src/main/res/layout/activity_wg_detail.xml index 013815abf..9d0dbd5bc 100644 --- a/app/src/main/res/layout/activity_wg_detail.xml +++ b/app/src/main/res/layout/activity_wg_detail.xml @@ -288,7 +288,6 @@ android:layout_marginTop="8dp" android:labelFor="@+id/config_public_key_text" android:text="@string/lbl_public_key" - android:textColor="?attr/primaryTextColor" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/config_title_layout" /> @@ -309,7 +308,6 @@ android:layout_marginTop="8dp" android:labelFor="@+id/config_addresses_text" android:text="@string/lbl_addresses" - android:textColor="?attr/primaryTextColor" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/public_key_text" /> From de36e1c1841bcd13bf95c1b79f8a5d1127ed1a4f Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Fri, 22 Mar 2024 22:29:12 +0530 Subject: [PATCH 21/81] ui: minor improvements in wg peer and iface edit screen --- .../bravedns/adapter/WgPeersAdapter.kt | 1 - .../ui/activity/WgConfigDetailActivity.kt | 5 +-- .../ui/activity/WgConfigEditorActivity.kt | 8 ----- .../res/layout/activity_wg_config_editor.xml | 32 +++---------------- .../main/res/layout/dialog_wg_add_peer.xml | 23 ++++++------- 5 files changed, 19 insertions(+), 50 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt index ee98b2bd7..49cb95593 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt @@ -36,7 +36,6 @@ import kotlinx.coroutines.withContext class WgPeersAdapter( val context: Context, - private val lifecycleOwner: LifecycleOwner, private val themeId: Int, private val configId: Int, private var peers: MutableList diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt index fc133d30a..743386c05 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt @@ -288,7 +288,8 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { if (it == 0) { b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentBad)) } - b.applicationsBtn.text = getString(R.string.add_remove_apps, it.toString()) + b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentGood)) + b.applicationsBtn.text = getString(R.string.add_remove_apps, it.toString()) } } @@ -439,7 +440,7 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { layoutManager = LinearLayoutManager(this) b.peersList.layoutManager = layoutManager val themeId = Themes.getCurrentTheme(isDarkThemeOn(), persistentState.theme) - wgPeersAdapter = WgPeersAdapter(this, this, themeId, configId, peers) + wgPeersAdapter = WgPeersAdapter(this, themeId, configId, peers) b.peersList.adapter = wgPeersAdapter } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt index 1831ffafe..729d1c75a 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt @@ -45,7 +45,6 @@ import org.koin.android.ext.android.inject class WgConfigEditorActivity : AppCompatActivity(R.layout.activity_wg_config_editor) { private val b by viewBinding(ActivityWgConfigEditorBinding::bind) private val persistentState by inject() - private val appConfig by inject() private var wgConfig: Config? = null private var wgInterface: WgInterface? = null @@ -84,7 +83,6 @@ class WgConfigEditorActivity : AppCompatActivity(R.layout.activity_wg_config_edi } private fun init() { - observeDnsName() io { wgConfig = WireguardManager.getConfigById(configId) wgInterface = wgConfig?.getInterface() @@ -119,12 +117,6 @@ class WgConfigEditorActivity : AppCompatActivity(R.layout.activity_wg_config_edi } } - private fun observeDnsName() { - appConfig.getConnectedDnsObservable().observe(this) { - b.wgWireguardDisclaimer.text = getString(R.string.wireguard_disclaimer, it) - } - } - private fun setupClickListeners() { b.privateKeyTextLayout.setEndIconOnClickListener { val key = Backend.newWgPrivateKey() diff --git a/app/src/main/res/layout/activity_wg_config_editor.xml b/app/src/main/res/layout/activity_wg_config_editor.xml index fc971eb34..8cbeae7f8 100644 --- a/app/src/main/res/layout/activity_wg_config_editor.xml +++ b/app/src/main/res/layout/activity_wg_config_editor.xml @@ -1,9 +1,10 @@ - - - - - + android:paddingEnd="5dp"> @@ -146,13 +147,13 @@ style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" android:layout_width="0dp" android:layout_height="wrap_content" - android:text="@string/lbl_dismiss" android:layout_marginTop="30dp" + android:text="@string/lbl_dismiss" android:textColor="?attr/accentGood" android:textSize="@dimen/large_font_text_view" - app:layout_constraintTop_toBottomOf="@+id/allowed_ips_label_layout" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toLeftOf="@+id/guideline" /> + app:layout_constraintRight_toLeftOf="@+id/guideline" + app:layout_constraintTop_toBottomOf="@+id/allowed_ips_label_layout" /> + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@+id/allowed_ips_label_layout" /> \ No newline at end of file From afb281d8acf6dc58b3795b5f7c04668b14edeff4 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 25 Mar 2024 17:31:08 +0530 Subject: [PATCH 22/81] remove unnecessary info logs from adapters --- .../java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt | 4 ---- .../java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt | 4 ---- .../java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt | 4 ---- .../com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt | 4 ---- 4 files changed, 16 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt index 46868bded..7267da032 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt @@ -113,10 +113,6 @@ class DoTEndpointAdapter( ) } b.endpointCheck.isChecked = endpoint.isSelected - Log.i( - LOG_TAG_DNS, - "connected to dot: ${endpoint.name} isSelected? ${endpoint.isSelected}" - ) if (endpoint.isSelected) { keepSelectedStatusUpdated() } else { diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt index 2ff92fd00..da8d94cb8 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt @@ -113,10 +113,6 @@ class DohEndpointAdapter( ) } b.endpointCheck.isChecked = endpoint.isSelected - Log.i( - LOG_TAG_DNS, - "connected to doh: ${endpoint.dohName} isSelected? ${endpoint.isSelected}" - ) if (endpoint.isSelected) { keepSelectedStatusUpdated() } else { diff --git a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt index 43e6d99ae..8062fd718 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt @@ -103,10 +103,6 @@ class ODoHEndpointAdapter( private fun displayDetails(endpoint: ODoHEndpoint) { b.endpointName.text = endpoint.name b.endpointCheck.isChecked = endpoint.isSelected - Log.i( - LOG_TAG_DNS, - "connected to ODoH: ${endpoint.name} isSelected? ${endpoint.isSelected}" - ) if (endpoint.isSelected) { keepSelectedStatusUpdated() } else { diff --git a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt index 7ded52f82..12027d463 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt @@ -112,10 +112,6 @@ class RethinkEndpointAdapter( private fun displayDetails(endpoint: RethinkDnsEndpoint) { b.rethinkEndpointListUrlName.text = endpoint.name b.rethinkEndpointListCheckImage.isChecked = endpoint.isActive - Log.i( - LOG_TAG_DNS, - "connected to rethink endpoint: ${endpoint.name} isSelected? ${endpoint.isActive}" - ) // Shows either the info/delete icon for the DoH entries. showIcon(endpoint) From fbb5c3c35c58fb04146137232e24ee82e6951879 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 25 Mar 2024 17:32:39 +0530 Subject: [PATCH 23/81] change custom rules for unknown apps from 'unnamed' to 'unknown' --- .../full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt index a52e29bcb..f61ce2cb0 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt @@ -293,7 +293,7 @@ class CustomIpAdapter(private val context: Context, private val type: CustomRule } if (appNames.isEmpty()) { - return context.getString(R.string.network_log_app_name_unnamed, "($uid)") + return context.getString(R.string.network_log_app_name_unknown) + " ($uid)" } val packageCount = appNames.count() From f118fec580f7b649c5f2bbc0608838e0d5d9a59b Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 25 Mar 2024 17:34:26 +0530 Subject: [PATCH 24/81] ui: include check for IPs possibly blocked in addition to 'hasBlocklist' validation --- .../java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt index 3b6b4010e..2bae212a5 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt @@ -44,6 +44,7 @@ import com.celzero.bravedns.ui.bottomsheet.DnsBlocklistBottomSheet import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DNS_LOG import com.celzero.bravedns.util.UIUtils.fetchColor +import com.celzero.bravedns.util.Utilities import com.google.gson.Gson class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : @@ -106,7 +107,7 @@ class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : b.queryLogIndicator.setBackgroundColor( ContextCompat.getColor(context, R.color.colorRed_A400) ) - } else if (dnsLog.blockLists.isNotEmpty()) { + } else if (determineMaybeBlocked(dnsLog)) { b.queryLogIndicator.visibility = View.VISIBLE val color = fetchColor(context, R.attr.chipTextNeutral) b.queryLogIndicator.setBackgroundColor(color) @@ -115,6 +116,12 @@ class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : } } + private fun determineMaybeBlocked(dnsLog: DnsLog): Boolean { + val anyRealIpBlocked = !dnsLog.responseIps.split(",").none { Utilities.isUnspecifiedIp(it.trim()) } + val hasBlocklist = dnsLog.blockLists.isNotEmpty() + return anyRealIpBlocked || hasBlocklist + } + private fun displayIcon(dnsLog: DnsLog) { b.flag.text = dnsLog.flag b.flag.visibility = View.VISIBLE From e5de7eb5681a7d08975f5bb3e634ce94ea2c1da3 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 25 Mar 2024 17:35:13 +0530 Subject: [PATCH 25/81] increase bug report file size from 10 to 30 --- .../java/com/celzero/bravedns/scheduler/BugReportZipper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt b/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt index 73b69089d..4b685c8d3 100644 --- a/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt +++ b/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt @@ -46,7 +46,7 @@ object BugReportZipper { private const val BUG_REPORT_FILE_NAME = "bugreport_" // maximum number of files allowed as part of bugreport zip file - private const val BUG_REPORT_MAX_FILES_ALLOWED = 10 + private const val BUG_REPORT_MAX_FILES_ALLOWED = 30 // secure sharing of files associated with an app, used in share bugreport file feature const val FILE_PROVIDER_NAME = BuildConfig.APPLICATION_ID + ".provider" @@ -196,7 +196,7 @@ object BugReportZipper { @RequiresApi(Build.VERSION_CODES.R) fun dumpAppExit(aei: ApplicationExitInfo, file: File) { val reportDetails = - "${aei.packageUid},${aei.reason},${aei.description},${aei.importance},${aei.pss},${aei.rss},${ + "${aei.packageUid}, reason: ${aei.reason}, desc: ${aei.description}, imp: ${aei.importance}, pss: ${aei.pss}, rss: ${aei.rss},${ Utilities.convertLongToTime(aei.timestamp, Constants.TIME_FORMAT_3) }\n" file.appendText(reportDetails) From add89da76c0ca1920099485a4a9d2b52a8b31ac3 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 25 Mar 2024 17:39:26 +0530 Subject: [PATCH 26/81] ui: include check for IPs possibly blocked in addition to 'hasBlocklist' validation --- .../ui/bottomsheet/DnsBlocklistBottomSheet.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt index 14c362816..8992103e0 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt @@ -62,10 +62,10 @@ import com.google.android.material.chip.Chip import com.google.common.collect.HashMultimap import com.google.common.collect.Multimap import com.google.gson.Gson +import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.android.ext.android.inject -import java.util.Locale class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { private var _binding: BottomSheetDnsLogBinding? = null @@ -269,7 +269,7 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { if (log!!.isBlocked) { lightenUpChip(b.dnsBlockBlocklistChip, BlockType.BLOCKED) - } else if (log!!.hasBlocklists()) { + } else if (determineMaybeBlocked()) { lightenUpChip(b.dnsBlockBlocklistChip, BlockType.MAYBE_BLOCKED) } else { lightenUpChip(b.dnsBlockBlocklistChip, BlockType.NONE) @@ -292,6 +292,18 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { return } + private fun determineMaybeBlocked(): Boolean { + if (log == null) { + Log.w(LOG_TAG_DNS_LOG, "Transaction detail missing, no need to update chips") + return false + } + + val anyRealIpBlocked = + !log!!.responseIps.split(",").none { Utilities.isUnspecifiedIp(it.trim()) } + val hasBlocklist = log!!.blockLists.isNotEmpty() + return anyRealIpBlocked || hasBlocklist + } + private fun showBlocklistChip() { val group: Multimap = HashMultimap.create() From 46b04f764a7fdd6167dcea2442c7c6989f86e07a Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 19:54:13 +0530 Subject: [PATCH 27/81] replace log.wtf with warn logs --- .../java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt | 2 +- .../com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt | 3 ++- .../full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt | 2 +- .../java/com/celzero/bravedns/adapter/RethinkLogAdapter.kt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt index fd93b5fdf..9604c2273 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt @@ -106,7 +106,7 @@ class AppConnectionAdapter(val context: Context, val lifecycleOwner: LifecycleOw private fun openBottomSheet(appConn: AppConnection) { if (context !is AppCompatActivity) { - Log.wtf(Logger.LOG_TAG_UI, "Error opening the app conn bottom sheet") + Log.w(Logger.LOG_TAG_UI, "Error opening the app conn bottom sheet") return } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt index cc0d365fb..8399428f3 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt @@ -34,6 +34,7 @@ import com.bumptech.glide.Glide import com.celzero.bravedns.R import com.celzero.bravedns.database.ConnectionTracker import com.celzero.bravedns.databinding.ConnectionTransactionRowBinding +import com.celzero.bravedns.service.BraveVPNService import com.celzero.bravedns.service.FirewallManager import com.celzero.bravedns.service.FirewallRuleset import com.celzero.bravedns.service.ProxyManager @@ -114,7 +115,7 @@ class ConnectionTrackerAdapter(private val context: Context) : private fun openBottomSheet(ct: ConnectionTracker) { if (context !is FragmentActivity) { - Log.wtf(LOG_TAG_UI, "err opening the connection tracker bottomsheet") + Log.w(LOG_TAG_UI, "err opening the connection tracker bottomsheet") return } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt index 2bae212a5..b26656d4c 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt @@ -160,7 +160,7 @@ class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : private fun openBottomSheet(dnsLog: DnsLog) { if (context !is FragmentActivity) { - Log.wtf( + Log.w( Logger.LOG_TAG_UI, "Can not open bottom sheet. Context is not attached to activity" ) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/RethinkLogAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/RethinkLogAdapter.kt index 7a25976fa..16f2ecec6 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/RethinkLogAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/RethinkLogAdapter.kt @@ -114,7 +114,7 @@ class RethinkLogAdapter(private val context: Context) : private fun openBottomSheet(log: RethinkLog) { if (context !is FragmentActivity) { - Log.wtf(LOG_TAG_UI, "Error opening the connection tracker bottomsheet") + Log.w(LOG_TAG_UI, "Error opening the connection tracker bottomsheet") return } From 403218ff6868ccea951289cbbca752484bf7ba84 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 19:56:05 +0530 Subject: [PATCH 28/81] ui: refresh DNS/proxies status every one second --- .../adapter/DnsCryptEndpointAdapter.kt | 33 +++-- .../bravedns/adapter/DoTEndpointAdapter.kt | 34 ++--- .../bravedns/adapter/DohEndpointAdapter.kt | 34 ++--- .../bravedns/adapter/ODoHEndpointAdapter.kt | 35 ++--- .../bravedns/adapter/OneWgConfigAdapter.kt | 81 ++++++++---- .../adapter/RethinkEndpointAdapter.kt | 35 ++--- .../bravedns/adapter/WgConfigAdapter.kt | 121 ++++++++++++++---- .../layout/list_item_wg_general_interface.xml | 97 ++++++++++---- .../res/layout/list_item_wg_one_interface.xml | 79 ++++++++++-- 9 files changed, 382 insertions(+), 167 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt index 77285d108..2959a5f9f 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsCryptEndpointAdapter.kt @@ -24,6 +24,7 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -44,18 +45,14 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class DnsCryptEndpointAdapter( - private val context: Context, - val lifecycleOwner: LifecycleOwner, - private val appConfig: AppConfig -) : +class DnsCryptEndpointAdapter(private val context: Context, private val appConfig: AppConfig) : PagingDataAdapter( DIFF_CALLBACK ) { - var statusCheckJob: Job = Job() + var lifecycleOwner: LifecycleOwner? = null companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { @@ -84,6 +81,7 @@ class DnsCryptEndpointAdapter( parent, false ) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return DnsCryptEndpointViewHolder(itemBinding) } @@ -94,6 +92,7 @@ class DnsCryptEndpointAdapter( inner class DnsCryptEndpointViewHolder(private val b: DnsCryptEndpointListItemBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(endpoint: DnsCryptEndpoint) { displayDetails(endpoint) @@ -139,7 +138,7 @@ class DnsCryptEndpointAdapter( statusCheckJob = ui { while (true) { updateSelectedStatus() - delay(DELAY) + delay(ONE_SEC) } } } @@ -147,11 +146,13 @@ class DnsCryptEndpointAdapter( private fun updateSelectedStatus() { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false || + bindingAdapterPosition == RecyclerView.NO_POSITION ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -238,8 +239,6 @@ class DnsCryptEndpointAdapter( endpoint.isSelected = true appConfig.handleDnscryptChanges(endpoint) } - // cancel the current job, new job will be created when the view is active - statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -259,12 +258,12 @@ class DnsCryptEndpointAdapter( withContext(Dispatchers.Main) { f() } } - private fun ui(f: suspend () -> Unit): Job { - return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + private fun ui(f: suspend () -> Unit): Job? { + return lifecycleOwner?.lifecycleScope?.launch(Dispatchers.Main) { f() } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } + lifecycleOwner?.lifecycleScope?.launch(Dispatchers.IO) { f() } } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt index 7267da032..7fb3522fc 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DoTEndpointAdapter.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -46,16 +47,13 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class DoTEndpointAdapter( - private val context: Context, - private val lifecycleOwner: LifecycleOwner, - private val appConfig: AppConfig -) : PagingDataAdapter(DIFF_CALLBACK) { +class DoTEndpointAdapter(private val context: Context, private val appConfig: AppConfig) : + PagingDataAdapter(DIFF_CALLBACK) { - var statusCheckJob: Job = Job() + var lifecycleOwner: LifecycleOwner? = null companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -79,6 +77,7 @@ class DoTEndpointAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DoTEndpointViewHolder { val itemBinding = ListItemEndpointBinding.inflate(LayoutInflater.from(parent.context), parent, false) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return DoTEndpointViewHolder(itemBinding) } @@ -89,6 +88,7 @@ class DoTEndpointAdapter( inner class DoTEndpointViewHolder(private val b: ListItemEndpointBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(endpoint: DoTEndpoint) { displayDetails(endpoint) @@ -127,7 +127,7 @@ class DoTEndpointAdapter( statusCheckJob = ui { while (true) { updateSelectedStatus() - delay(DELAY) + delay(ONE_SEC) } } } @@ -135,11 +135,13 @@ class DoTEndpointAdapter( private fun updateSelectedStatus() { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false || + bindingAdapterPosition == RecyclerView.NO_POSITION ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -171,8 +173,6 @@ class DoTEndpointAdapter( endpoint.isSelected = true appConfig.handleDoTChanges(endpoint) } - // cancel the current job, new job will be created when the view is active - statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -252,12 +252,12 @@ class DoTEndpointAdapter( withContext(Dispatchers.Main) { f() } } - private fun ui(f: suspend () -> Unit): Job { - return lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job? { + return lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.Main) { f() } } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.IO) { f() } } + lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.IO) { f() } } } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt index da8d94cb8..2645abb00 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DohEndpointAdapter.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -46,16 +47,13 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class DohEndpointAdapter( - private val context: Context, - private val lifecycleOwner: LifecycleOwner, - private val appConfig: AppConfig -) : PagingDataAdapter(DIFF_CALLBACK) { +class DohEndpointAdapter(private val context: Context, private val appConfig: AppConfig) : + PagingDataAdapter(DIFF_CALLBACK) { - var statusCheckJob: Job = Job() + var lifecycleOwner: LifecycleOwner? = null companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -79,6 +77,7 @@ class DohEndpointAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DoHEndpointViewHolder { val itemBinding = ListItemEndpointBinding.inflate(LayoutInflater.from(parent.context), parent, false) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return DoHEndpointViewHolder(itemBinding) } @@ -89,6 +88,7 @@ class DohEndpointAdapter( inner class DoHEndpointViewHolder(private val b: ListItemEndpointBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(endpoint: DoHEndpoint) { displayDetails(endpoint) @@ -127,7 +127,7 @@ class DohEndpointAdapter( statusCheckJob = ui { while (true) { updateSelectedStatus() - delay(DELAY) + delay(ONE_SEC) } } } @@ -135,11 +135,13 @@ class DohEndpointAdapter( private fun updateSelectedStatus() { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false || + bindingAdapterPosition == RecyclerView.NO_POSITION ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -171,8 +173,6 @@ class DohEndpointAdapter( endpoint.isSelected = true appConfig.handleDoHChanges(endpoint) } - // cancel the current job, new job will be created when the view is active - statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -252,12 +252,12 @@ class DohEndpointAdapter( withContext(Dispatchers.Main) { f() } } - private fun ui(f: suspend () -> Unit): Job { - return lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + private fun ui(f: suspend () -> Unit): Job? { + return lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.Main) { f() } } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } + lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.IO) { f() } } } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt index 8062fd718..0f1ee21a4 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ODoHEndpointAdapter.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -46,15 +47,13 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class ODoHEndpointAdapter( - private val context: Context, - private val lifecycleOwner: LifecycleOwner, - private val appConfig: AppConfig -) : PagingDataAdapter(DIFF_CALLBACK) { +class ODoHEndpointAdapter(private val context: Context, private val appConfig: AppConfig) : + PagingDataAdapter(DIFF_CALLBACK) { + + var lifecycleOwner: LifecycleOwner? = null - var statusCheckJob: Job = Job() companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -78,6 +77,7 @@ class ODoHEndpointAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ODoHEndpointViewHolder { val itemBinding = ListItemEndpointBinding.inflate(LayoutInflater.from(parent.context), parent, false) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return ODoHEndpointViewHolder(itemBinding) } @@ -88,6 +88,7 @@ class ODoHEndpointAdapter( inner class ODoHEndpointViewHolder(private val b: ListItemEndpointBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(endpoint: ODoHEndpoint) { displayDetails(endpoint) @@ -117,7 +118,7 @@ class ODoHEndpointAdapter( statusCheckJob = ui { while (true) { updateSelectedStatus() - delay(DELAY) + delay(ONE_SEC) } } } @@ -125,11 +126,13 @@ class ODoHEndpointAdapter( private fun updateSelectedStatus() { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false || + bindingAdapterPosition == RecyclerView.NO_POSITION ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -161,8 +164,6 @@ class ODoHEndpointAdapter( endpoint.isSelected = true appConfig.handleODoHChanges(endpoint) } - // cancel the current job, new job will be created when the view is active - statusCheckJob.cancel() } private fun deleteEndpoint(id: Int) { @@ -253,12 +254,12 @@ class ODoHEndpointAdapter( withContext(Dispatchers.Main) { f() } } - private fun ui(f: suspend () -> Unit): Job { - return lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.Main) { f() } } + private fun ui(f: suspend () -> Unit): Job? { + return lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.Main) { f() } } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch { withContext(Dispatchers.IO) { f() } } + lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.IO) { f() } } } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt index e409b4488..66afdea6f 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt @@ -21,6 +21,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope @@ -49,7 +50,6 @@ import kotlinx.coroutines.withContext class OneWgConfigAdapter(private val context: Context, private val listener: DnsStatusListener) : PagingDataAdapter(DIFF_CALLBACK) { - private var statusCheckJob: Job? = Job() private var lifecycleOwner: LifecycleOwner? = null interface DnsStatusListener { @@ -57,7 +57,7 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns } companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { @@ -99,6 +99,7 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns inner class WgInterfaceViewHolder(private val b: ListItemWgOneInterfaceBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(config: WgConfigFiles) { b.interfaceNameText.text = config.name @@ -108,9 +109,9 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns if (config.oneWireGuard) { keepStatusUpdated(config) } else { - statusCheckJob?.cancel() b.interfaceDetailCard.strokeWidth = 0 b.interfaceAppsCount.visibility = View.GONE + b.protocolInfoChipGroup.visibility = View.GONE b.oneWgCheck.isChecked = false b.interfaceStatus.text = context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) @@ -121,32 +122,67 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns statusCheckJob = ui { while (true) { updateStatus(config) - delay(DELAY) + delay(ONE_SEC) } } } - private fun updateStatus(config: WgConfigFiles) { - val id = ProxyManager.ID_WG_BASE + config.id - val apps = ProxyManager.getAppCountForProxy(id).toString() - val statusId = VpnController.getProxyStatusById(id) - updateStatusUi(config, statusId, apps) + private fun updateProtocolChip(pair: Pair) { + if (b.protocolInfoChipGroup.isVisible) return + + if (!pair.first && !pair.second) { + b.protocolInfoChipGroup.visibility = View.GONE + return + } + b.protocolInfoChipGroup.visibility = View.VISIBLE + if (pair.first) { + b.protocolInfoChipIpv4.visibility = View.VISIBLE + } else { + b.protocolInfoChipIpv4.visibility = View.GONE + } + if (pair.second) { + b.protocolInfoChipIpv6.visibility = View.VISIBLE + } else { + b.protocolInfoChipIpv6.visibility = View.GONE + } } - private fun updateStatusUi(config: WgConfigFiles, statusId: Long?, apps: String) { + private fun updateSplitTunnelChip(isSplitTunnel: Boolean) { + if (isSplitTunnel) { + b.chipSplitTunnel.visibility = View.VISIBLE + } else { + b.chipSplitTunnel.visibility = View.GONE + } + } + + private fun updateStatus(config: WgConfigFiles) { // if the view is not active then cancel the job if ( - lifecycleOwner != null && - lifecycleOwner - ?.lifecycle - ?.currentState - ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false ) { statusCheckJob?.cancel() return } - val appsCount = context.getString(R.string.firewall_card_status_active, apps) + val id = ProxyManager.ID_WG_BASE + config.id + val statusId = VpnController.getProxyStatusById(id) + val pair = VpnController.getSupportedIpVersion(id) + val c = WireguardManager.getConfigById(config.id) + val isSplitTunnel = + if (c?.getPeers()?.isNotEmpty() == true) { + VpnController.isSplitTunnelProxy(id, pair) + } else { + false + } + updateStatusUi(config, statusId) + updateProtocolChip(pair) + updateSplitTunnelChip(isSplitTunnel) + } + + private fun updateStatusUi(config: WgConfigFiles, statusId: Long?) { if (config.isActive) { b.interfaceDetailCard.strokeColor = fetchColor(context, R.color.accentGood) b.interfaceDetailCard.strokeWidth = 2 @@ -171,14 +207,10 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns b.interfaceStatus.text = context.getString(resId).replaceFirstChar(Char::titlecase) } else { + b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextNegative) + b.interfaceDetailCard.strokeWidth = 2 b.interfaceStatus.text = - context.getString( - R.string.about_version_install_source, - context - .getString(R.string.status_waiting) - .replaceFirstChar(Char::titlecase), - appsCount - ) + context.getString(R.string.status_waiting).replaceFirstChar(Char::titlecase) } } else { b.interfaceDetailCard.strokeWidth = 0 @@ -235,9 +267,6 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns } private fun ui(f: suspend () -> Unit): Job? { - if (lifecycleOwner == null) { - return null - } return lifecycleOwner?.lifecycleScope?.launch(Dispatchers.Main) { f() } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt index 12027d463..abc36a76d 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/RethinkEndpointAdapter.kt @@ -25,6 +25,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil @@ -47,19 +48,17 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext -class RethinkEndpointAdapter( - private val context: Context, - private val lifecycleOwner: LifecycleOwner, - private val appConfig: AppConfig -) : +class RethinkEndpointAdapter(private val context: Context, private val appConfig: AppConfig) : PagingDataAdapter( DIFF_CALLBACK ) { - var statusCheckJob: Job = Job() + var lifecycleOwner: LifecycleOwner? = null + companion object { - private const val DELAY = 1000L + private const val ONE_SEC = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { override fun areItemsTheSame( @@ -87,6 +86,7 @@ class RethinkEndpointAdapter( parent, false ) + lifecycleOwner = parent.findViewTreeLifecycleOwner() return RethinkEndpointViewHolder(itemBinding) } @@ -97,6 +97,7 @@ class RethinkEndpointAdapter( inner class RethinkEndpointViewHolder(private val b: RethinkEndpointListItemBinding) : RecyclerView.ViewHolder(b.root) { + private var statusCheckJob: Job? = null fun update(endpoint: RethinkDnsEndpoint) { displayDetails(endpoint) @@ -127,7 +128,7 @@ class RethinkEndpointAdapter( ui { while (true) { updateBlocklistStatusText(endpoint) - delay(DELAY) + delay(ONE_SEC) } } } @@ -135,11 +136,13 @@ class RethinkEndpointAdapter( private fun updateBlocklistStatusText(endpoint: RethinkDnsEndpoint) { // if the view is not active then cancel the job if ( - !lifecycleOwner.lifecycle.currentState.isAtLeast( - androidx.lifecycle.Lifecycle.State.STARTED - ) + lifecycleOwner + ?.lifecycle + ?.currentState + ?.isAtLeast(androidx.lifecycle.Lifecycle.State.STARTED) == false || + bindingAdapterPosition == RecyclerView.NO_POSITION ) { - statusCheckJob.cancel() + statusCheckJob?.cancel() return } @@ -186,8 +189,6 @@ class RethinkEndpointAdapter( endpoint.isActive = true appConfig.handleRethinkChanges(endpoint) } - // cancel the current job, new job will be created when the view is active - statusCheckJob.cancel() } private fun showDohMetadataDialog(endpoint: RethinkDnsEndpoint) { @@ -236,12 +237,12 @@ class RethinkEndpointAdapter( context.startActivity(intent) } - private fun ui(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { f() } + private fun ui(f: suspend () -> Unit): Job? { + return lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.Main) { f() } } } private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } + lifecycleOwner?.lifecycleScope?.launch { withContext(Dispatchers.IO) { f() } } } } } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt index 016a714c3..865670c87 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt @@ -38,6 +38,7 @@ import com.celzero.bravedns.ui.activity.WgConfigDetailActivity import com.celzero.bravedns.ui.activity.WgConfigEditorActivity.Companion.INTENT_EXTRA_WG_ID import com.celzero.bravedns.util.UIUtils import com.celzero.bravedns.util.Utilities +import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -46,11 +47,11 @@ import kotlinx.coroutines.launch class WgConfigAdapter(private val context: Context) : PagingDataAdapter(DIFF_CALLBACK) { - private var configs: MutableMap = mutableMapOf() + private var configs: ConcurrentHashMap = ConcurrentHashMap() private var lifecycleOwner: LifecycleOwner? = null companion object { - private const val DELAY = 1000L + private const val ONE_SEC_MS = 1000L private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { @@ -91,11 +92,9 @@ class WgConfigAdapter(private val context: Context) : return WgInterfaceViewHolder(itemBinding) } - override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { - super.onDetachedFromRecyclerView(recyclerView) - configs.values.forEach { - it.cancel() - } + override fun onViewDetachedFromWindow(holder: WgInterfaceViewHolder) { + super.onViewDetachedFromWindow(holder) + configs.values.forEach { it.cancel() } configs.clear() } @@ -118,27 +117,77 @@ class WgConfigAdapter(private val context: Context) : configs[config.id] = job } } else { - b.interfaceCatchAll.visibility = View.GONE - b.interfaceLockdown.visibility = View.GONE + disableInactiveConfig(config) + } + } + + private fun disableInactiveConfig(config: WgConfigFiles) { + // if lockdown is enabled, then show the lockdown card even if config is disabled + if (config.isLockdown) { + b.protocolInfoChipGroup.visibility = View.GONE + b.interfaceConfigStatus.text = + context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) + val id = ProxyManager.ID_WG_BASE + config.id + val appsCount = ProxyManager.getAppCountForProxy(id) + updateUi(config, appsCount) + } else { + b.interfaceStatus.visibility = View.GONE + b.interfaceAppsCount.visibility = View.GONE b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.background) b.interfaceDetailCard.strokeWidth = 0 b.interfaceSwitch.isChecked = false - b.interfaceStatus.text = + b.protocolInfoChipGroup.visibility = View.GONE + b.interfaceConfigStatus.visibility = View.VISIBLE + b.interfaceConfigStatus.text = context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) - // cancel the job if it already exists for the config, as the config is disabled - cancelJobIfAny(config.id) } + // cancel the job if it already exists for the config, as the config is disabled + cancelJobIfAny(config.id) } private fun updateProxyStatusContinuously(config: WgConfigFiles): Job? { return ui { while (true) { updateStatus(config) - delay(DELAY) + delay(ONE_SEC_MS) } } } + private fun updateProtocolChip(pair: Pair?) { + if (pair == null) return + + if (!pair.first && !pair.second) { + b.protocolInfoChipGroup.visibility = View.GONE + b.protocolInfoChipIpv4.visibility = View.GONE + b.protocolInfoChipIpv6.visibility = View.GONE + return + } + b.protocolInfoChipGroup.visibility = View.VISIBLE + b.protocolInfoChipIpv4.visibility = View.GONE + b.protocolInfoChipIpv6.visibility = View.GONE + if (pair.first) { + b.protocolInfoChipIpv4.visibility = View.VISIBLE + b.protocolInfoChipIpv4.text = context.getString(R.string.settings_ip_text_ipv4) + } else { + b.protocolInfoChipIpv4.visibility = View.GONE + } + if (pair.second) { + b.protocolInfoChipIpv6.visibility = View.VISIBLE + b.protocolInfoChipIpv6.text = context.getString(R.string.settings_ip_text_ipv6) + } else { + b.protocolInfoChipIpv6.visibility = View.GONE + } + } + + private fun updateSplitTunnelChip(isSplitTunnel: Boolean) { + if (isSplitTunnel) { + b.chipSplitTunnel.visibility = View.VISIBLE + } else { + b.chipSplitTunnel.visibility = View.GONE + } + } + private fun cancelJobIfAny(id: Int) { val job = configs[id] job?.cancel() @@ -146,9 +195,7 @@ class WgConfigAdapter(private val context: Context) : } private fun cancelAllJobs() { - configs.values.forEach { - it.cancel() - } + configs.values.forEach { it.cancel() } configs.clear() } @@ -156,6 +203,13 @@ class WgConfigAdapter(private val context: Context) : val id = ProxyManager.ID_WG_BASE + config.id val appsCount = ProxyManager.getAppCountForProxy(id) val statusId = VpnController.getProxyStatusById(id) + val pair = VpnController.getSupportedIpVersion(id) + val c = WireguardManager.getConfigById(config.id) + val isSplitTunnel = if (c?.getPeers()?.isNotEmpty() == true) { + VpnController.isSplitTunnelProxy(id, pair) + } else { + false + } // if the view is not active then cancel the job if ( @@ -168,28 +222,36 @@ class WgConfigAdapter(private val context: Context) : cancelAllJobs() return } - updateUi(config, appsCount) updateStatusUi(config, statusId) + updateUi(config, appsCount) + updateProtocolChip(pair) + updateSplitTunnelChip(isSplitTunnel) } private fun updateUi(config: WgConfigFiles, appsCount: Int) { + b.interfaceAppsCount.visibility = View.VISIBLE if (config.isCatchAll) { - b.interfaceCatchAll.visibility = View.VISIBLE - b.interfaceLockdown.visibility = View.GONE + b.interfaceConfigStatus.visibility = View.VISIBLE b.interfaceAppsCount.text = context.getString(R.string.routing_remaining_apps) b.interfaceAppsCount.setTextColor( UIUtils.fetchColor(context, R.attr.primaryLightColorText) ) - b.interfaceCatchAll.text = context.getString(R.string.catch_all_wg_dialog_title) + b.interfaceConfigStatus.text = context.getString(R.string.catch_all_wg_dialog_title) return // no need to update the apps count } else if (config.isLockdown) { - b.interfaceCatchAll.visibility = View.GONE - b.interfaceLockdown.visibility = View.VISIBLE - b.interfaceLockdown.text = context.getString(R.string.firewall_rule_global_lockdown) + b.interfaceDetailCard.strokeWidth = 2 + b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) + b.interfaceConfigStatus.visibility = View.VISIBLE + b.interfaceConfigStatus.text = context.getString(R.string.firewall_rule_global_lockdown) } else { - b.interfaceCatchAll.visibility = View.GONE - b.interfaceLockdown.visibility = View.GONE + b.interfaceConfigStatus.visibility = View.GONE } + if (!config.isActive) { + // no need to update the apps count if the config is disabled + b.interfaceAppsCount.visibility = View.GONE + return + } + b.interfaceAppsCount.text = context.getString(R.string.firewall_card_status_active, appsCount.toString()) if (appsCount == 0) { @@ -205,6 +267,10 @@ class WgConfigAdapter(private val context: Context) : if (config.isActive) { b.interfaceSwitch.isChecked = true b.interfaceDetailCard.strokeWidth = 2 + b.interfaceStatus.visibility = View.VISIBLE + b.interfaceConfigStatus.visibility = View.VISIBLE + b.interfaceConfigStatus.text = + context.getString(R.string.lbl_active).replaceFirstChar(Char::titlecase) if (statusId != null) { val resId = UIUtils.getProxyStatusStringRes(statusId) // change the color based on the status @@ -231,7 +297,10 @@ class WgConfigAdapter(private val context: Context) : b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.background) b.interfaceDetailCard.strokeWidth = 0 b.interfaceSwitch.isChecked = false - b.interfaceStatus.text = + b.interfaceStatus.visibility = View.GONE + b.interfaceAppsCount.visibility = View.GONE + b.interfaceConfigStatus.visibility = View.VISIBLE + b.interfaceConfigStatus.text = context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) } } diff --git a/app/src/main/res/layout/list_item_wg_general_interface.xml b/app/src/main/res/layout/list_item_wg_general_interface.xml index b0dbe9900..96c301b6a 100644 --- a/app/src/main/res/layout/list_item_wg_general_interface.xml +++ b/app/src/main/res/layout/list_item_wg_general_interface.xml @@ -37,15 +37,78 @@ android:orientation="vertical" android:padding="5dp"> - + android:orientation="horizontal"> + + + + + + + + + + + + + + + android:textSize="@dimen/default_font_text_view" + android:textStyle="italic" /> - - + android:textSize="@dimen/default_font_text_view" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/list_item_wg_one_interface.xml b/app/src/main/res/layout/list_item_wg_one_interface.xml index d531e10d1..414a95ac4 100644 --- a/app/src/main/res/layout/list_item_wg_one_interface.xml +++ b/app/src/main/res/layout/list_item_wg_one_interface.xml @@ -37,15 +37,77 @@ android:orientation="vertical" android:padding="5dp"> - + android:orientation="horizontal"> + + + + + + + + + + + + + + android:textSize="@dimen/default_font_text_view" + android:textStyle="italic" /> Date: Wed, 27 Mar 2024 19:57:35 +0530 Subject: [PATCH 29/81] add missing external storage permission for pcap --- app/src/full/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index 305a61a3b..50167af9e 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools"> + Date: Wed, 27 Mar 2024 20:06:45 +0530 Subject: [PATCH 30/81] ui: introduce a fresh activity for displaying supplementary app-specific logs --- app/src/full/AndroidManifest.xml | 2 + .../bravedns/ui/activity/AppInfoActivity.kt | 35 +++++--- .../ui/activity/AppWiseLogsActivity.kt | 88 +++++++++++++++++++ .../viewmodel/AppConnectionsViewModel.kt | 59 +++++++++---- .../full/res/layout/activity_app_details.xml | 25 +++++- .../bravedns/database/ConnectionTrackerDAO.kt | 16 +++- .../res/layout/activity_app_wise_logs.xml | 51 +++++++++++ 7 files changed, 243 insertions(+), 33 deletions(-) create mode 100644 app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt create mode 100644 app/src/main/res/layout/activity_app_wise_logs.xml diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index 50167af9e..a9de24e02 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -106,6 +106,8 @@ + LOG_THRESHOLD_SIZE) { + b.aadConnShowAllChip.visibility = View.VISIBLE + } else { + b.aadConnShowAllChip.visibility = View.GONE + } } } } @@ -192,10 +201,6 @@ class AppInfoActivity : b.aadIpBlockCard.visibility = View.GONE } - override fun onResume() { - super.onResume() - b.aadConnDetailRecycler.adapter?.notifyDataSetChanged() - } private fun openCustomIpScreen() { val intent = Intent(this, CustomRulesActivity::class.java) @@ -423,6 +428,12 @@ class AppInfoActivity : b.aadAppDnsRethinkConfigure.setOnClickListener { rethinkListBottomSheet() } b.aadConnDelete.setOnClickListener { showDeleteConnectionsDialog() } + + b.aadConnShowAllChip.setOnClickListener { + val intent = Intent(this, AppWiseLogsActivity::class.java) + intent.putExtra(UID_INTENT_NAME, uid) + startActivity(intent) + } } private fun showAppInfoDialog(packages: List) { @@ -583,10 +594,10 @@ class AppInfoActivity : networkLogsViewModel.appNetworkLogs.observe(this) { recyclerAdapter.submitData(this.lifecycle, it) } - b.aadConnDetailRecycler.isNestedScrollingEnabled = false + b.aadConnDetailRecycler.isNestedScrollingEnabled = true b.aadConnDetailRecycler.adapter = recyclerAdapter val itemAnimator = DefaultItemAnimator() - itemAnimator.changeDuration = 1500 + itemAnimator.changeDuration = 1000 b.aadConnDetailRecycler.itemAnimator = itemAnimator } @@ -821,8 +832,8 @@ class AppInfoActivity : Configuration.UI_MODE_NIGHT_YES } - private fun io(f: suspend () -> Unit) { - lifecycleScope.launch(Dispatchers.IO) { f() } + private fun io(f: suspend () -> Unit): Job { + return lifecycleScope.launch(Dispatchers.IO) { f() } } private suspend fun uiCtx(f: suspend () -> Unit) { @@ -830,12 +841,16 @@ class AppInfoActivity : } override fun onQueryTextSubmit(query: String): Boolean { - networkLogsViewModel.setFilter(query) + networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.OFFSET) return true } override fun onQueryTextChange(query: String): Boolean { - networkLogsViewModel.setFilter(query) + Utilities.delay(500, lifecycleScope) { + if (!this.isFinishing) { + networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.OFFSET) + } + } return true } } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt new file mode 100644 index 000000000..ad5939145 --- /dev/null +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2024 RethinkDNS and its 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. + */ +package com.celzero.bravedns.ui.activity + +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.SearchView +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import by.kirich1409.viewbindingdelegate.viewBinding +import com.celzero.bravedns.R +import com.celzero.bravedns.adapter.AppConnectionAdapter +import com.celzero.bravedns.databinding.ActivityAppWiseLogsBinding +import com.celzero.bravedns.service.PersistentState +import com.celzero.bravedns.util.Constants +import com.celzero.bravedns.util.Themes +import com.celzero.bravedns.util.Utilities +import com.celzero.bravedns.viewmodel.AppConnectionsViewModel +import org.koin.android.ext.android.inject +import org.koin.androidx.viewmodel.ext.android.viewModel + +class AppWiseLogsActivity : + AppCompatActivity(R.layout.activity_app_wise_logs), SearchView.OnQueryTextListener { + private val b by viewBinding(ActivityAppWiseLogsBinding::bind) + + private val persistentState by inject() + private val networkLogsViewModel: AppConnectionsViewModel by viewModel() + private var uid: Int = Constants.INVALID_UID + private var layoutManager: RecyclerView.LayoutManager? = null + + private fun Context.isDarkThemeOn(): Boolean { + return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == + Configuration.UI_MODE_NIGHT_YES + } + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(Themes.getCurrentTheme(isDarkThemeOn(), persistentState.theme)) + super.onCreate(savedInstanceState) + uid = intent.getIntExtra(AppInfoActivity.UID_INTENT_NAME, Constants.INVALID_UID) + if (uid == Constants.INVALID_UID) { + finish() + } + b.awlSearch.setOnQueryTextListener(this) + setAdapter() + } + + private fun setAdapter() { + networkLogsViewModel.setUid(uid) + b.awlRecyclerConnection.setHasFixedSize(true) + layoutManager = LinearLayoutManager(this) + b.awlRecyclerConnection.layoutManager = layoutManager + val recyclerAdapter = AppConnectionAdapter(this, this, uid) + networkLogsViewModel.allAppNetworkLogs.observe(this) { + recyclerAdapter.submitData(this.lifecycle, it) + } + b.awlRecyclerConnection.adapter = recyclerAdapter + } + + override fun onQueryTextSubmit(query: String): Boolean { + networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.ALL) + return true + } + + override fun onQueryTextChange(query: String): Boolean { + Utilities.delay(500, lifecycleScope) { + if (!this.isFinishing) { + networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.ALL) + } + } + return true + } +} diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt index 2d06b8ab9..014c3b94c 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt @@ -10,60 +10,87 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn +import androidx.paging.liveData import com.celzero.bravedns.data.AppConnection import com.celzero.bravedns.database.ConnectionTrackerDAO import com.celzero.bravedns.util.Constants -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flatMapLatest class AppConnectionsViewModel(private val nwlogDao: ConnectionTrackerDAO) : ViewModel() { private var filter: MutableLiveData = MutableLiveData() - private var uid: Int = Constants.MISSING_UID + private var allLogsFilter: MutableLiveData = MutableLiveData() + private var uid: Int = Constants.INVALID_UID private val pagingConfig: PagingConfig - var f: StateFlow init { filter.value = "" - f = filter.value?.let { MutableStateFlow(it) } ?: MutableStateFlow("") + allLogsFilter.value = "" pagingConfig = PagingConfig( - pageSize = Constants.LIVEDATA_PAGE_SIZE, - prefetchDistance = Constants.LIVEDATA_PAGE_SIZE / 2, - enablePlaceholders = false + enablePlaceholders = false, + prefetchDistance = 10, + initialLoadSize = Constants.LIVEDATA_PAGE_SIZE, + pageSize = Constants.LIVEDATA_PAGE_SIZE ) } + enum class FilterType { + OFFSET, + ALL + } + val appNetworkLogs = filter.switchMap { input -> fetchNetworkLogs(uid, input) } + val allAppNetworkLogs = allLogsFilter.switchMap { input -> fetchAllNetworkLogs(uid, input) } + private fun fetchNetworkLogs(uid: Int, input: String): LiveData> { val pager = if (input.isEmpty()) { - Pager(config = pagingConfig, pagingSourceFactory = { nwlogDao.getLogsForApp(uid) }) + Pager(config = pagingConfig, pagingSourceFactory = { nwlogDao.getLogsForAppWithLimit(uid) }) .flow .cachedIn(viewModelScope) } else { Pager( config = pagingConfig, - pagingSourceFactory = { nwlogDao.getLogsForAppFiltered(uid, "%$input%") } + pagingSourceFactory = { nwlogDao.getLogsForAppFilteredWithLimit(uid, "%$input%") } ) .flow .cachedIn(viewModelScope) } return pager.asLiveData() + + /*return if (input.isEmpty()) { + Pager(pagingConfig) { nwlogDao.getLogsForAppWithLimit(uid) } + .liveData + .cachedIn(viewModelScope) + } else { + Pager(pagingConfig) { nwlogDao.getLogsForAppFilteredWithLimit(uid, "%$input%") } + .liveData + .cachedIn(viewModelScope) + }*/ + } + + private fun fetchAllNetworkLogs(uid: Int, input: String): LiveData> { + return if (input.isEmpty()) { + Pager(pagingConfig) { nwlogDao.getAllLogs(uid) }.liveData.cachedIn(viewModelScope) + } else { + Pager(pagingConfig) { nwlogDao.getAllLogsFiltered(uid, "%$input%") } + .liveData + .cachedIn(viewModelScope) + } } fun getConnectionsCount(uid: Int): LiveData { return nwlogDao.getAppConnectionsCount(uid) } - fun setFilter(input: String) { - this.filter.postValue(input) - this.f = MutableStateFlow(input) + fun setFilter(input: String, filterType: FilterType) { + if (filterType == FilterType.OFFSET) { + this.filter.postValue(input) + } else { + this.allLogsFilter.postValue(input) + } } fun setUid(uid: Int) { diff --git a/app/src/full/res/layout/activity_app_details.xml b/app/src/full/res/layout/activity_app_details.xml index c4a004203..eb5e050a2 100644 --- a/app/src/full/res/layout/activity_app_details.xml +++ b/app/src/full/res/layout/activity_app_details.xml @@ -527,29 +527,46 @@ android:focusableInTouchMode="true" android:visibility="visible"> - - + + + + @Query( + "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid GROUP BY ipAddress, uid, port ORDER BY count DESC LIMIT 100 OFFSET 0" + ) + fun getLogsForAppWithLimit(uid: Int): PagingSource + @Query( "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid GROUP BY ipAddress, uid, port ORDER BY count DESC" ) - fun getLogsForApp(uid: Int): PagingSource + fun getAllLogs(uid: Int): PagingSource + + @Query( + "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid and ipAddress like :query GROUP BY ipAddress, uid, port ORDER BY count DESC" + ) + fun getAllLogsFiltered(uid: Int, query: String): PagingSource @Query( - "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid and ipAddress like :ipAddress GROUP BY ipAddress, uid, port ORDER BY count DESC" + "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid and ipAddress like :ipAddress GROUP BY ipAddress, uid, port ORDER BY count DESC LIMIT 100 OFFSET 0" ) - fun getLogsForAppFiltered(uid: Int, ipAddress: String): PagingSource + fun getLogsForAppFilteredWithLimit(uid: Int, ipAddress: String): PagingSource @Query("select count(DISTINCT(ipAddress)) from ConnectionTracker where uid = :uid") fun getAppConnectionsCount(uid: Int): LiveData diff --git a/app/src/main/res/layout/activity_app_wise_logs.xml b/app/src/main/res/layout/activity_app_wise_logs.xml new file mode 100644 index 000000000..20ccbe301 --- /dev/null +++ b/app/src/main/res/layout/activity_app_wise_logs.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + From 2261d89c948cc6e78320ce29bed279b0e29c89c9 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:07:06 +0530 Subject: [PATCH 31/81] bump firestack version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b8a43a45c..567e50606 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,8 +247,8 @@ dependencies { fullImplementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9' // from: https://jitpack.io/#celzero/firestack - download 'com.github.celzero:firestack:ee9609571f@aar' - implementation 'com.github.celzero:firestack:ee9609571f@aar' + download 'com.github.celzero:firestack:b29ea51b53@aar' + implementation 'com.github.celzero:firestack:b29ea51b53@aar' // Work manager implementation('androidx.work:work-runtime-ktx:2.9.0') { From 712647379aeed55ef54d2adcba2b3f02c4d36278 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:08:46 +0530 Subject: [PATCH 32/81] fix: log stmt, rmv unuset persistent variable --- .../full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt | 2 +- .../main/java/com/celzero/bravedns/service/NetLogTracker.kt | 1 - app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt | 1 + .../main/java/com/celzero/bravedns/service/PersistentState.kt | 3 --- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt index 7d5dc7a82..f71b7dd29 100644 --- a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt +++ b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt @@ -116,7 +116,7 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : ) } return if (isRetryRequired(retryCount) && paymentStatus == TcpProxyHelper.PaymentStatus.INITIATED) { - Log.i(LOG_TAG_DOWNLOAD, "retrying the downloadRemoteBlocklist") + Log.i(LOG_TAG_DOWNLOAD, "retrying the payment status check") getPaymentStatusFromServer(retryCount + 1) } else { Log.i(LOG_TAG_DOWNLOAD, "retry count exceeded, returning null") diff --git a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt index 113c93fbf..ac8a35b4c 100644 --- a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt @@ -48,7 +48,6 @@ internal constructor( private val dnsLatencyTracker by inject() private var scope: CoroutineScope? = null - private set private var dnsLogTracker: DnsLogTracker? = null private var ipTracker: IPTracker? = null diff --git a/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt b/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt index cfefa4219..208dcbb55 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong +// should this be replaced with developer.android.com/reference/android/os/CountDownTimer? object PauseTimer { // default duration for pause state: 15mins diff --git a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt index a3a1fb425..6af8aa9fb 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt @@ -244,9 +244,6 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { // enable dns alg var enableDnsAlg by booleanPref("dns_alg").withDefault(false) - // dns crypt relay server - var dnscryptRelays by stringPref("dnscrypt_relay").withDefault("") - // default dns url var defaultDnsUrl by stringPref("default_dns_query").withDefault("") From 83894e7667570e7f6520bf345be51dd13375b8f2 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:10:17 +0530 Subject: [PATCH 33/81] remove addition of wg to tunnel when setting one-wg --- .../full/java/com/celzero/bravedns/service/WireguardManager.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt index 7276bb77a..e362be16f 100644 --- a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt @@ -581,9 +581,6 @@ object WireguardManager : KoinComponent { Log.i(LOG_TAG_PROXY, "update one wg, id: $id, ${config.getName()} to $owg") db.updateOneWireGuardConfig(id, owg) map?.oneWireGuard = owg - if (map?.isActive == true) { - VpnController.addWireGuardProxy(id = ProxyManager.ID_WG_BASE + config.getId()) - } } suspend fun addPeer(id: Int, peer: Peer) { From a7e062e54d78602aeb41b53918bc183df24aabaf Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:14:16 +0530 Subject: [PATCH 34/81] fix: Orbot HTTP update failure, implement retry mechanism on failure --- .../ui/activity/ProxySettingsActivity.kt | 7 +-- .../com/celzero/bravedns/util/Constants.kt | 4 +- .../com/celzero/bravedns/util/OrbotHelper.kt | 58 +++++++++++-------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt index ef34938d7..b2cf2ef39 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt @@ -488,9 +488,8 @@ class ProxySettingsActivity : AppCompatActivity(R.layout.fragment_proxy_configur getString( R.string.ci_ip_label, it.getName(), - getString(R.string.status_waiting).padStart(1, ' ') - ) - .replaceFirstChar(Char::titlecase) + "\n" + getString(R.string.status_waiting).replaceFirstChar(Char::titlecase).padStart(1, ' ') + ) + "\n" if (DEBUG) Log.d(LOG_TAG_PROXY, "current proxy status is null for $id") } } @@ -687,7 +686,7 @@ class ProxySettingsActivity : AppCompatActivity(R.layout.fragment_proxy_configur } } else { ipAddressEditText.setText(Constants.SOCKS_DEFAULT_IP, TextView.BufferType.EDITABLE) - portEditText.setText(Constants.SOCKS_DEFAULT_PORT, TextView.BufferType.EDITABLE) + portEditText.setText(Constants.SOCKS_DEFAULT_PORT.toString(), TextView.BufferType.EDITABLE) } headerTxt.text = getString(R.string.settings_dns_proxy_dialog_header) diff --git a/app/src/main/java/com/celzero/bravedns/util/Constants.kt b/app/src/main/java/com/celzero/bravedns/util/Constants.kt index 1fb6edcc3..776601d26 100644 --- a/app/src/main/java/com/celzero/bravedns/util/Constants.kt +++ b/app/src/main/java/com/celzero/bravedns/util/Constants.kt @@ -170,13 +170,13 @@ class Constants { const val SEARCH_QUERY = "search_query" // default custom http proxy port number - const val HTTP_PROXY_PORT = "8118" + const val HTTP_PROXY_PORT = 8118 // default custom socks5 ip const val SOCKS_DEFAULT_IP = "127.0.0.1" // default custom socks5 port - const val SOCKS_DEFAULT_PORT = "9050" + const val SOCKS_DEFAULT_PORT = 9050 // constants to send type of proxy: for socks5 const val SOCKS = "Socks5" diff --git a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt index d8d43d941..26c785b19 100644 --- a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt +++ b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt @@ -40,21 +40,25 @@ import com.celzero.bravedns.receiver.NotificationActionReceiver import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.service.ProxyManager import com.celzero.bravedns.ui.HomeScreenActivity +import com.celzero.bravedns.util.Constants.Companion.HTTP_PROXY_PORT +import com.celzero.bravedns.util.Constants.Companion.SOCKS_DEFAULT_IP +import com.celzero.bravedns.util.Constants.Companion.SOCKS_DEFAULT_PORT import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_VPN import com.celzero.bravedns.util.Utilities.getActivityPendingIntent import com.celzero.bravedns.util.Utilities.getBroadcastPendingIntent import com.celzero.bravedns.util.Utilities.isAtleastO import com.celzero.bravedns.util.Utilities.isAtleastT +import com.celzero.bravedns.util.Utilities.isAtleastU import com.celzero.bravedns.util.Utilities.isFdroidFlavour import com.celzero.bravedns.util.Utilities.isPlayStoreFlavour import com.celzero.bravedns.util.Utilities.isValidPort +import java.net.URI +import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.net.URI -import java.util.concurrent.TimeUnit /** * One-click Orbot setup. @@ -111,14 +115,16 @@ class OrbotHelper( const val NOTIF_CHANNEL_ID_PROXY_ALERTS = "PROXY_ALERTS" } - var socks5Port: Int? = null - var httpsPort: Int? = null - var socks5Ip: String? = null - var httpsIp: String? = null - var dnsPort: Int? = null + private var socks5Port: Int? = null + private var httpsPort: Int? = null + private var socks5Ip: String? = null + private var httpsIp: String? = null + private var dnsPort: Int? = null @Volatile private var isResponseReceivedFromOrbot: Boolean = false + private var retryCount = 3 + /** Returns the intent which will initiate the Orbot in non-vpn mode. */ private fun getOrbotStartIntent(): Intent { val intent = Intent(ACTION_START) @@ -186,7 +192,9 @@ class OrbotHelper( selectedProxyType = type val intent = getOrbotStartIntent() isResponseReceivedFromOrbot = false - context.registerReceiver(orbotStatusReceiver, IntentFilter(ACTION_STATUS)) + ContextCompat.registerReceiver(context, orbotStatusReceiver, IntentFilter(ACTION_STATUS), + ContextCompat.RECEIVER_EXPORTED + ) context.sendBroadcast(intent) waitForOrbot() if (DEBUG) Log.d(LOG_TAG_VPN, "request orbot start by broadcast") @@ -215,15 +223,7 @@ class OrbotHelper( when (status) { STATUS_ON -> { isResponseReceivedFromOrbot = true - if ( - socks5Ip == null || - httpsIp == null || - socks5Port == null || - httpsPort == null || - dnsPort == null - ) { - updateOrbotProxyData(intent) - } + updateOrbotProxyData(intent) setOrbotMode() } STATUS_OFF -> { @@ -428,8 +428,8 @@ class OrbotHelper( /** Get the data from the received intent from the Orbot and assign the values. */ fun updateOrbotProxyData(data: Intent?) { - socks5Port = data?.getIntExtra(EXTRA_SOCKS_PROXY_PORT, 0) - httpsPort = data?.getIntExtra(EXTRA_HTTP_PROXY_PORT, 0) + socks5Port = data?.getIntExtra(EXTRA_SOCKS_PROXY_PORT, SOCKS_DEFAULT_PORT) + httpsPort = data?.getIntExtra(EXTRA_HTTP_PROXY_PORT, HTTP_PROXY_PORT) socks5Ip = data?.getStringExtra(EXTRA_SOCKS_PROXY_HOST) httpsIp = data?.getStringExtra(EXTRA_HTTP_PROXY_HOST) dnsPort = data?.getIntExtra(EXTRA_DNS_PORT, 0) @@ -437,27 +437,37 @@ class OrbotHelper( if (DEBUG) Log.d( LOG_TAG_VPN, - "OrbotHelper - Orbot - socks5:$socks5Ip($socks5Port), http: $httpsIp($httpsPort), dns port: $dnsPort" + "OrbotHelper: new val socks5:$socks5Ip($socks5Port), http: $httpsIp($httpsPort), dns: $dnsPort" ) } + /** * Create a ScheduledExecutorService which will be executed after 30 sec of Orbot status * initiation. */ private suspend fun waitForOrbot() { io { - delay(TimeUnit.SECONDS.toMillis(25L)) + delay(TimeUnit.SECONDS.toMillis(15L)) Log.i(LOG_TAG_VPN, "after timeout, isOrbotUp? $isResponseReceivedFromOrbot") - // Execute a task in the background thread after 25 sec. + // Execute a task in the background thread after 15 sec. // If there is no response for the broadcast from the Orbot, // then the executor will execute and will disable the Orbot settings. // Some cases where the Orbot won't be responding -eg., force stop of application, // user disabled auto-start of the application. - if (isResponseReceivedFromOrbot) return@io + if (isResponseReceivedFromOrbot) { + retryCount = 3 // reset the retry count + return@io + } - withContext(Dispatchers.Main) { stopOrbot(isInteractive = false) } + retryCount-- + if (retryCount > 0) { + Log.i(LOG_TAG_VPN, "retrying orbot start") + startOrbot(selectedProxyType) + } else { + uiCtx { stopOrbot(isInteractive = false) } + } } } From 98f92ea335394451e633fb66ca71ef3c5fea9444 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:16:46 +0530 Subject: [PATCH 35/81] ui: set default DNS for wg to 1.1.1.1 if unavailable --- .../bravedns/ui/activity/WgConfigEditorActivity.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt index 729d1c75a..718448f91 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigEditorActivity.kt @@ -25,7 +25,6 @@ import androidx.lifecycle.lifecycleScope import backend.Backend import by.kirich1409.viewbindingdelegate.viewBinding import com.celzero.bravedns.R -import com.celzero.bravedns.data.AppConfig import com.celzero.bravedns.databinding.ActivityWgConfigEditorBinding import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.service.WireguardManager @@ -54,7 +53,10 @@ class WgConfigEditorActivity : AppCompatActivity(R.layout.activity_wg_config_edi companion object { const val INTENT_EXTRA_WG_ID = "WIREGUARD_TUNNEL_ID" private const val CLIPBOARD_PUBLIC_KEY_LBL = "Public Key" - private const val DEFAULT_MTU = "1500" + private const val DEFAULT_MTU = "1280" + // when dns is set to auto, the default dns is set to 1.1.1.1. this differs from official + // wireguard for android, because rethink requires a dns to be set in "Simple" mode + private const val DEFAULT_DNS = "1.1.1.1" private const val DEFAULT_LISTEN_PORT = "0" } @@ -131,7 +133,7 @@ class WgConfigEditorActivity : AppCompatActivity(R.layout.activity_wg_config_edi val addresses = b.addressesLabelText.text.toString() val mtu = b.mtuText.text.toString().ifEmpty { DEFAULT_MTU } val listenPort = b.listenPortText.text.toString().ifEmpty { DEFAULT_LISTEN_PORT } - val dnsServers = b.dnsServersText.text.toString() + val dnsServers = b.dnsServersText.text.toString().ifEmpty { DEFAULT_DNS } val privateKey = b.privateKeyText.text.toString() io { val isInterfaceAdded = From c13121dcd52f04b867468db385ea660f34c6e421 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:18:03 +0530 Subject: [PATCH 36/81] ui: notify one wg adapter along with advanced screen --- .../full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt index 3de523492..476661b83 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt @@ -235,6 +235,7 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main), OneW override fun onResume() { super.onResume() + oneWgConfigAdapter?.notifyDataSetChanged() wgConfigAdapter?.notifyDataSetChanged() } From ce3874c66dccf94208c78f5d01d7c481befd4919 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:19:56 +0530 Subject: [PATCH 37/81] fix: remove addition of lifecycle owner to adapter --- .../celzero/bravedns/ui/bottomsheet/RethinkListBottomSheet.kt | 2 +- .../com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt | 2 +- .../java/com/celzero/bravedns/ui/fragment/DoTListFragment.kt | 2 +- .../java/com/celzero/bravedns/ui/fragment/DohListFragment.kt | 2 +- .../java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt | 2 +- .../com/celzero/bravedns/ui/fragment/RethinkListFragment.kt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/RethinkListBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/RethinkListBottomSheet.kt index b8b71da55..e9a08b87b 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/RethinkListBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/RethinkListBottomSheet.kt @@ -77,7 +77,7 @@ class RethinkListBottomSheet : BottomSheetDialogFragment() { layoutManager = LinearLayoutManager(requireContext()) b.bsrRethinkListRecycler.layoutManager = layoutManager - recyclerAdapter = RethinkEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + recyclerAdapter = RethinkEndpointAdapter(requireContext(), get()) viewModel.setFilter(filter) viewModel.rethinkEndpointList.observe(viewLifecycleOwner) { recyclerAdapter!!.submitData(viewLifecycleOwner.lifecycle, it) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt index 02ae0571d..b4ecc4878 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt @@ -82,7 +82,7 @@ class DnsCryptListFragment : Fragment(R.layout.fragment_dns_crypt_list) { b.recyclerDnsCryptConnections.layoutManager = dnsCryptLayoutManager dnsCryptRecyclerAdapter = - DnsCryptEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + DnsCryptEndpointAdapter(requireContext(), get()) dnsCryptViewModel.dnsCryptEndpointList.observe(viewLifecycleOwner) { dnsCryptRecyclerAdapter.submitData(viewLifecycleOwner.lifecycle, it) } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DoTListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DoTListFragment.kt index 11be53daa..6d887f828 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DoTListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DoTListFragment.kt @@ -63,7 +63,7 @@ class DoTListFragment : Fragment(R.layout.fragment_dot_list) { layoutManager = LinearLayoutManager(requireContext()) b.recyclerDot.layoutManager = layoutManager - adapter = DoTEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + adapter = DoTEndpointAdapter(requireContext(), get()) viewModel.dohEndpointList.observe(viewLifecycleOwner) { adapter!!.submitData(viewLifecycleOwner.lifecycle, it) } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DohListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DohListFragment.kt index 44d3ae4d6..9fe2ba61a 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DohListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DohListFragment.kt @@ -66,7 +66,7 @@ class DohListFragment : Fragment(R.layout.fragment_doh_list) { layoutManager = LinearLayoutManager(requireContext()) b.recyclerDohConnections.layoutManager = layoutManager - dohRecyclerAdapter = DohEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + dohRecyclerAdapter = DohEndpointAdapter(requireContext(), get()) viewModel.dohEndpointList.observe(viewLifecycleOwner) { dohRecyclerAdapter!!.submitData(viewLifecycleOwner.lifecycle, it) } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt index 5707e2332..f59c4423e 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt @@ -65,7 +65,7 @@ class ODoHListFragment : Fragment(R.layout.fragment_odoh_list) { layoutManager = LinearLayoutManager(requireContext()) b.recyclerOdoh.layoutManager = layoutManager - adapter = ODoHEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + adapter = ODoHEndpointAdapter(requireContext(), get()) viewModel.dohEndpointList.observe(viewLifecycleOwner) { adapter!!.submitData(viewLifecycleOwner.lifecycle, it) } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkListFragment.kt index 664f8d7cb..d20524204 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkListFragment.kt @@ -171,7 +171,7 @@ class RethinkListFragment : Fragment(R.layout.fragment_rethink_list) { layoutManager = LinearLayoutManager(requireContext()) b.recyclerDohConnections.layoutManager = layoutManager - recyclerAdapter = RethinkEndpointAdapter(requireContext(), viewLifecycleOwner, get()) + recyclerAdapter = RethinkEndpointAdapter(requireContext(), get()) viewModel.setFilter(uid) viewModel.rethinkEndpointList.observe(viewLifecycleOwner) { recyclerAdapter!!.submitData(viewLifecycleOwner.lifecycle, it) From 818fb347234dda8d7b39ee36ec495f60e0084c27 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:22:34 +0530 Subject: [PATCH 38/81] add support always on meta data for vpnservice --- app/src/main/AndroidManifest.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7baaf9eb0..ea25a54ae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,9 +8,9 @@ - - @@ -55,6 +55,9 @@ + Date: Wed, 27 Mar 2024 20:24:11 +0530 Subject: [PATCH 39/81] add delay to onQueryTextChange before setting filter --- .../bravedns/ui/fragment/ConnectionTrackerFragment.kt | 9 +++++++-- .../com/celzero/bravedns/ui/fragment/DnsLogFragment.kt | 9 +++++++-- .../celzero/bravedns/ui/fragment/RethinkLogFragment.kt | 7 ++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/ConnectionTrackerFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/ConnectionTrackerFragment.kt index 8d79e5a3a..a4d97029e 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/ConnectionTrackerFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/ConnectionTrackerFragment.kt @@ -36,6 +36,7 @@ import com.celzero.bravedns.service.FirewallRuleset import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.util.Constants import com.celzero.bravedns.util.UIUtils.formatToRelativeTime +import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.viewmodel.ConnectionTrackerViewModel import com.celzero.bravedns.viewmodel.ConnectionTrackerViewModel.TopLevelFilter import com.google.android.material.chip.Chip @@ -247,8 +248,12 @@ class ConnectionTrackerFragment : } override fun onQueryTextChange(query: String): Boolean { - this.filterQuery = query - viewModel.setFilter(query, filterCategories, filterType) + Utilities.delay(500, lifecycleScope) { + if (this.isAdded) { + this.filterQuery = query + viewModel.setFilter(query, filterCategories, filterType) + } + } return true } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsLogFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsLogFragment.kt index 8850cab88..f38a0c875 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsLogFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsLogFragment.kt @@ -35,6 +35,7 @@ import com.celzero.bravedns.databinding.FragmentDnsLogsBinding import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.util.Constants import com.celzero.bravedns.util.UIUtils.formatToRelativeTime +import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.viewmodel.DnsLogViewModel import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -253,8 +254,12 @@ class DnsLogFragment : Fragment(R.layout.fragment_dns_logs), SearchView.OnQueryT } override fun onQueryTextChange(query: String): Boolean { - this.filterValue = query - viewModel.setFilter(filterValue, filterType) + Utilities.delay(500, lifecycleScope) { + if (this.isAdded) { + this.filterValue = query + viewModel.setFilter(filterValue, filterType) + } + } return true } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkLogFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkLogFragment.kt index 1714a9a7c..43d232109 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkLogFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkLogFragment.kt @@ -32,6 +32,7 @@ import com.celzero.bravedns.databinding.ActivityConnectionTrackerBinding import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.util.Constants import com.celzero.bravedns.util.UIUtils.formatToRelativeTime +import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.viewmodel.RethinkLogViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers @@ -133,7 +134,11 @@ class RethinkLogFragment : } override fun onQueryTextChange(query: String): Boolean { - viewModel.setFilter(query) + Utilities.delay(500, lifecycleScope) { + if (this.isAdded) { + viewModel.setFilter(query) + } + } return true } From d9925c0ce9c5b4194d6d6b3b43b783af65893e12 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:25:32 +0530 Subject: [PATCH 40/81] ui: include remote/local type name in apply button --- .../bravedns/ui/fragment/RethinkBlocklistFragment.kt | 11 ++++++++++- .../main/res/layout/fragment_rethink_blocklist.xml | 12 ++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt index 6fc81d785..c954ff70e 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt @@ -74,12 +74,12 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.util.regex.Pattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -import java.util.regex.Pattern class RethinkBlocklistFragment : Fragment(R.layout.fragment_rethink_blocklist), SearchView.OnQueryTextListener { @@ -203,6 +203,15 @@ class RethinkBlocklistFragment : private fun init() { modifiedStamp = getStamp() + val typeName = + if (type.isLocal()) { + getString(R.string.lbl_on_device) + } else { + getString(R.string.rdns_plus) + } + b.lbBlocklistApplyBtn.text = + getString(R.string.ct_ip_details, getString(R.string.lbl_apply), typeName) + updateFileTagList(emptySet()) // update ui based on blocklist availability diff --git a/app/src/main/res/layout/fragment_rethink_blocklist.xml b/app/src/main/res/layout/fragment_rethink_blocklist.xml index 3085c7ddf..41afbd220 100644 --- a/app/src/main/res/layout/fragment_rethink_blocklist.xml +++ b/app/src/main/res/layout/fragment_rethink_blocklist.xml @@ -181,6 +181,12 @@ android:visibility="gone" /> + + From b6d03235c00563a932ff0fb79f683d5b9e3cb304 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:27:50 +0530 Subject: [PATCH 41/81] method to verify if the port number corresponds to DoT --- .../main/java/com/celzero/bravedns/util/KnownPorts.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt b/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt index 40f67df51..cb748414b 100644 --- a/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt +++ b/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt @@ -23,6 +23,7 @@ class KnownPorts { private const val NTP_PORT: Int = 123 const val HTTP_PORT: Int = 80 const val HTTPS_PORT: Int = 443 + const val DOT_PORT: Int = 853 // represents the unknown port in the port map. see class KnownPorts const val PORT_VAL_UNKNOWN = "unknown" @@ -35,11 +36,15 @@ class KnownPorts { } fun isNtp(port: Int): Boolean { - return portMap[port] == portMap[NTP_PORT] + return port == NTP_PORT } fun isDns(port: Int): Boolean { - return portMap[port] == portMap[DNS_PORT] + return port == DNS_PORT + } + + fun isDoT(port: Int): Boolean { + return port == DOT_PORT } // init hash map with reserved ports (1-1024) and protocol identifiers From f5de2018704eaa46ffcb65536c010f197636e728 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:29:14 +0530 Subject: [PATCH 42/81] correct default dns value for dns.google in persistent state --- .../java/com/celzero/bravedns/ui/HomeScreenActivity.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/full/java/com/celzero/bravedns/ui/HomeScreenActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/HomeScreenActivity.kt index 015de8faf..645adeb06 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/HomeScreenActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/HomeScreenActivity.kt @@ -370,6 +370,14 @@ class HomeScreenActivity : AppCompatActivity(R.layout.activity_home_screen) { } private fun removeThisMethod() { + // change the persistent state for defaultDnsUrl, if its google.com (only for v055d) + // fixme: remove this post v054. + // this is to fix the default dns url, as the default dns url is changed from + // dns.google.com to dns.google. In servers.xml default ips available for dns.google + // so changing the default dns url to dns.google + if (persistentState.defaultDnsUrl.contains("dns.google.com")) { + persistentState.defaultDnsUrl = Constants.DEFAULT_DNS_LIST[2].url + } moveRemoteBlocklistFileFromAsset() // reset the bio metric auth time, as now the value is changed from System.currentTimeMillis // to SystemClock.elapsedRealtime From 4f3db9e89d0dd0b919a0801d16704ad754ac45c5 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:36:57 +0530 Subject: [PATCH 43/81] ui: hide rules when the app is in rethink mode --- .../bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt index 7a4eea898..615da287f 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt @@ -142,6 +142,16 @@ class ConnTrackerBottomSheet : BottomSheetDialogFragment(), KoinComponent { displaySummaryDetails() // setup click and item selected listeners setupClickListeners() + + val rethinkUid = Utilities.getApplicationInfo(requireContext(), requireContext().packageName)?.uid ?: Constants.INVALID_UID + if (info!!.uid == rethinkUid) { + // do not show rules for RethinkDNS app + b.bsConnBlockedRule1HeaderLl.visibility = View.GONE + b.bsConnBlockedRule2HeaderLl.visibility = View.GONE + b.bsConnBlockedRule3HeaderLl.visibility = View.GONE + b.bsConnDomainRuleLl.visibility = View.GONE + return + } // updates the ip rules button updateIpRulesUi(info!!.uid, info!!.ipAddress, info!!.port) // updates the value from dns request cache if available From 2827b3dace483876e4edfe152654432d0c3b87fc Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:45:34 +0530 Subject: [PATCH 44/81] rmv runBLocking from ConnectionTracer --- .../com/celzero/bravedns/net/manager/ConnectionTracer.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt b/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt index 4b36cdc97..17433411b 100644 --- a/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt +++ b/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt @@ -45,7 +45,7 @@ class ConnectionTracer(ctx: Context) { } @TargetApi(Build.VERSION_CODES.Q) - fun getUidQ( + suspend fun getUidQ( protocol: Int, sourceIp: String, sourcePort: Int, @@ -80,8 +80,8 @@ class ConnectionTracer(ctx: Context) { } val key = makeCacheKey(protocol, local, remote) try { - // executing inside a coroutine to avoid the NetworkOnMainThreadException issue#853 - runBlocking(Dispatchers.IO) { uid = cm.getConnectionOwnerUid(protocol, local, remote) } + // must be called from io thread to avoid the NetworkOnMainThreadException issue#853 + uid = cm.getConnectionOwnerUid(protocol, local, remote) if (DEBUG) Log.d( From 5ca8532925c593b0cb0ac66f45ff425cd5ddf620 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Wed, 27 Mar 2024 20:46:53 +0530 Subject: [PATCH 45/81] rmv unused persistent variable (dnscryptRelays) --- app/src/main/java/com/celzero/bravedns/data/AppConfig.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt b/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt index ca93287f9..0ffd74d4c 100644 --- a/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt +++ b/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt @@ -74,6 +74,9 @@ internal constructor( private var connectedDns: MutableLiveData = MutableLiveData() private const val ORBOT_DNS = "Orbot" + + const val LOOPBACK_DNS = "localhost" // also default fallback ip + const val RINR_FALLBACK_DNS = "8.8.4.4" } init { @@ -701,7 +704,6 @@ internal constructor( suspend fun handleDnsrelayChanges(endpoint: DnsCryptRelayEndpoint) { dnsCryptRelayEndpointRepository.update(endpoint) - persistentState.dnscryptRelays = getDnscryptRelayServers() } suspend fun removeDnscryptRelay(stamp: String) { From 7f083417f4e6a1a88ec4f4be1c14aed342a48d63 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:36:03 +0530 Subject: [PATCH 46/81] ui: mark for split proxy and include active time duration --- .../bravedns/adapter/OneWgConfigAdapter.kt | 37 +++++++++++-- .../bravedns/adapter/WgConfigAdapter.kt | 53 +++++++++++++++---- .../bravedns/service/WireguardManager.kt | 36 ++++++++----- .../layout/list_item_wg_general_interface.xml | 18 +++++-- .../res/layout/list_item_wg_one_interface.xml | 20 +++++-- 5 files changed, 129 insertions(+), 35 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt index 66afdea6f..2e99c9589 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt @@ -17,6 +17,9 @@ package com.celzero.bravedns.adapter import android.content.Context import android.content.Intent +import android.os.SystemClock +import android.text.format.DateUtils +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -189,6 +192,7 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns b.oneWgCheck.isChecked = true b.interfaceAppsCount.visibility = View.VISIBLE b.interfaceAppsCount.text = context.getString(R.string.one_wg_apps_added) + var status = "" if (statusId != null) { val resId = UIUtils.getProxyStatusStringRes(statusId) // change the color based on the status @@ -204,23 +208,50 @@ class OneWgConfigAdapter(private val context: Context, private val listener: Dns b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextNegative) } - b.interfaceStatus.text = - context.getString(resId).replaceFirstChar(Char::titlecase) + status = context.getString(resId).replaceFirstChar(Char::titlecase) } else { b.interfaceDetailCard.strokeColor = fetchColor(context, R.attr.chipTextNegative) b.interfaceDetailCard.strokeWidth = 2 - b.interfaceStatus.text = + status = context.getString(R.string.status_waiting).replaceFirstChar(Char::titlecase) } + b.interfaceStatus.text = status + b.interfaceActiveUptime.visibility = View.VISIBLE + val time = getUpTime(config.id) + if (time.isNotEmpty()) { + val t = context.getString(R.string.logs_card_duration, getUpTime(config.id)) + b.interfaceActiveUptime.text = + context.getString( + R.string.two_argument_space, + context.getString(R.string.lbl_active), + t + ) + } else { + b.interfaceActiveUptime.text = context.getString(R.string.lbl_active) + } } else { b.interfaceDetailCard.strokeWidth = 0 b.interfaceAppsCount.visibility = View.GONE b.oneWgCheck.isChecked = false + b.interfaceActiveUptime.visibility = View.GONE b.interfaceStatus.text = context.getString(R.string.lbl_disabled).replaceFirstChar(Char::titlecase) } } + private fun getUpTime(id: Int): CharSequence { + val startTime = WireguardManager.getActiveConfigTimestamp(id) ?: return "" + val now = System.currentTimeMillis() + val uptimeMs = SystemClock.elapsedRealtime() - startTime + // returns a string describing 'time' as a time relative to 'now' + return DateUtils.getRelativeTimeSpanString( + now - uptimeMs, + now, + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE + ) + } + fun setupClickListeners(config: WgConfigFiles) { b.interfaceDetailCard.setOnClickListener { launchConfigDetail(config.id) } diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt index 865670c87..03007d0ea 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt @@ -17,6 +17,9 @@ package com.celzero.bravedns.adapter import android.content.Context import android.content.Intent +import android.os.SystemClock +import android.text.format.DateUtils +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -205,11 +208,12 @@ class WgConfigAdapter(private val context: Context) : val statusId = VpnController.getProxyStatusById(id) val pair = VpnController.getSupportedIpVersion(id) val c = WireguardManager.getConfigById(config.id) - val isSplitTunnel = if (c?.getPeers()?.isNotEmpty() == true) { - VpnController.isSplitTunnelProxy(id, pair) - } else { - false - } + val isSplitTunnel = + if (c?.getPeers()?.isNotEmpty() == true) { + VpnController.isSplitTunnelProxy(id, pair) + } else { + false + } // if the view is not active then cancel the job if ( @@ -242,7 +246,8 @@ class WgConfigAdapter(private val context: Context) : b.interfaceDetailCard.strokeWidth = 2 b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) b.interfaceConfigStatus.visibility = View.VISIBLE - b.interfaceConfigStatus.text = context.getString(R.string.firewall_rule_global_lockdown) + b.interfaceConfigStatus.text = + context.getString(R.string.firewall_rule_global_lockdown) } else { b.interfaceConfigStatus.visibility = View.GONE } @@ -269,8 +274,20 @@ class WgConfigAdapter(private val context: Context) : b.interfaceDetailCard.strokeWidth = 2 b.interfaceStatus.visibility = View.VISIBLE b.interfaceConfigStatus.visibility = View.VISIBLE - b.interfaceConfigStatus.text = - context.getString(R.string.lbl_active).replaceFirstChar(Char::titlecase) + var status = "" + b.interfaceActiveUptime.visibility = View.VISIBLE + val time = getUpTime(config.id) + if (time.isNotEmpty()) { + val t = context.getString(R.string.logs_card_duration, getUpTime(config.id)) + b.interfaceActiveUptime.text = + context.getString( + R.string.two_argument_space, + context.getString(R.string.lbl_active), + t + ) + } else { + b.interfaceActiveUptime.text = context.getString(R.string.lbl_active) + } if (statusId != null) { val resId = UIUtils.getProxyStatusStringRes(statusId) // change the color based on the status @@ -285,15 +302,16 @@ class WgConfigAdapter(private val context: Context) : b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) } - b.interfaceStatus.text = - context.getString(resId).replaceFirstChar(Char::titlecase) + status = context.getString(resId).replaceFirstChar(Char::titlecase) } else { b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) - b.interfaceStatus.text = + status = context.getString(R.string.status_waiting).replaceFirstChar(Char::titlecase) } + b.interfaceStatus.text = status } else { + b.interfaceActiveUptime.visibility = View.GONE b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.background) b.interfaceDetailCard.strokeWidth = 0 b.interfaceSwitch.isChecked = false @@ -305,6 +323,19 @@ class WgConfigAdapter(private val context: Context) : } } + private fun getUpTime(id: Int): CharSequence { + val startTime = WireguardManager.getActiveConfigTimestamp(id) ?: return "" + val now = System.currentTimeMillis() + val uptimeMs = SystemClock.elapsedRealtime() - startTime + // returns a string describing 'time' as a time relative to 'now' + return DateUtils.getRelativeTimeSpanString( + now - uptimeMs, + now, + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE + ) + } + fun setupClickListeners(config: WgConfigFiles) { b.interfaceDetailCard.setOnClickListener { launchConfigDetail(config.id) } diff --git a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt index e362be16f..75fa346d7 100644 --- a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt @@ -16,6 +16,7 @@ package com.celzero.bravedns.service import android.content.Context +import android.os.SystemClock import android.util.Log import backend.Backend import backend.WgKey @@ -65,6 +66,10 @@ object WireguardManager : KoinComponent { private const val JSON_RESPONSE_WORKS = "works" private const val JSON_RESPONSE_REASON = "reason" private const val JSON_RESPONSE_QUOTA = "quota" + + // map to store the active wireguard configs timestamp for the active time calculation + private val activeConfigTimestamps = HashMap() + // warp primary and secondary config names, ids and file names const val SEC_WARP_NAME = "SEC_WARP" const val SEC_WARP_ID = 0 @@ -231,19 +236,6 @@ object WireguardManager : KoinComponent { return config.getName() } - fun disableConfig(id: String) { - if (isConfigActive(id)) { - val s = convertStringIdToId(id) - val config = mappings.find { it.id == s } - - if (config != null) { - disableConfig(config) - } - } else { - Log.w(LOG_TAG_PROXY, "Config not active: $id") - } - } - suspend fun disableAllActiveConfigs() { val activeConfigs = mappings.filter { it.isActive } activeConfigs.forEach { @@ -791,6 +783,24 @@ object WireguardManager : KoinComponent { return false } + fun getActiveConfigTimestamp(configId: Int): Long? { + return activeConfigTimestamps[configId] + } + + fun setActiveConfigTimestamp(configId: String, timestamp: Long) { + val id = convertStringIdToId(configId) + activeConfigTimestamps[id] = timestamp + } + + fun removeActiveConfigTimestamp(configId: String) { + val id = convertStringIdToId(configId) + activeConfigTimestamps.remove(id) + } + + fun clearActiveConfigTimestamps() { + activeConfigTimestamps.clear() + } + private fun io(f: suspend () -> Unit) { CoroutineScope(Dispatchers.IO).launch { f() } } diff --git a/app/src/main/res/layout/list_item_wg_general_interface.xml b/app/src/main/res/layout/list_item_wg_general_interface.xml index 96c301b6a..905e5368f 100644 --- a/app/src/main/res/layout/list_item_wg_general_interface.xml +++ b/app/src/main/res/layout/list_item_wg_general_interface.xml @@ -49,8 +49,8 @@ android:layout_gravity="center" android:layout_marginStart="3dp" android:layout_marginEnd="3dp" - android:padding="5dp" android:maxLength="15" + android:padding="5dp" android:textAppearance="?attr/textAppearanceHeadline6" android:textColor="?attr/secondaryTextColor" /> @@ -60,9 +60,9 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="3dp" - android:visibility="gone" android:layout_marginEnd="5dp" - android:paddingBottom="2dp"> + android:paddingBottom="2dp" + android:visibility="gone"> + + @@ -58,11 +58,11 @@ android:id="@+id/protocol_info_chip_group" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="5dp" android:layout_gravity="center" - android:visibility="gone" + android:layout_marginStart="5dp" android:layout_marginEnd="5dp" - android:paddingBottom="2dp"> + android:paddingBottom="2dp" + android:visibility="gone"> + + Date: Thu, 28 Mar 2024 22:39:33 +0530 Subject: [PATCH 47/81] new param to determine whether the upstream DNS request is blocked or not --- .../com/celzero/bravedns/adapter/DnsQueryAdapter.kt | 4 +--- .../ui/bottomsheet/DnsBlocklistBottomSheet.kt | 8 ++------ .../java/com/celzero/bravedns/database/DnsLog.kt | 1 + .../com/celzero/bravedns/database/LogDatabase.kt | 13 ++++++++++++- .../com/celzero/bravedns/net/doh/Transaction.kt | 1 + .../com/celzero/bravedns/service/DnsLogTracker.kt | 5 ++++- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt index b26656d4c..9e49036a5 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt @@ -117,9 +117,7 @@ class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : } private fun determineMaybeBlocked(dnsLog: DnsLog): Boolean { - val anyRealIpBlocked = !dnsLog.responseIps.split(",").none { Utilities.isUnspecifiedIp(it.trim()) } - val hasBlocklist = dnsLog.blockLists.isNotEmpty() - return anyRealIpBlocked || hasBlocklist + return dnsLog.upstreamBlock || dnsLog.blockLists.isNotEmpty() } private fun displayIcon(dnsLog: DnsLog) { diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt index 8992103e0..b17fbef67 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt @@ -77,7 +77,6 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { private var log: DnsLog? = null private val persistentState by inject() - private val appConfig by inject() override fun getTheme(): Int = Themes.getBottomsheetCurrentTheme(isDarkThemeOn(), persistentState.theme) @@ -223,7 +222,7 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { displayDescription() - if (log!!.groundedQuery() || log!!.hasBlocklists()) { + if (log!!.groundedQuery() || log!!.hasBlocklists() || log!!.upstreamBlock) { handleBlocklistChip() b.dnsBlockIpsChip.visibility = View.GONE return @@ -298,10 +297,7 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { return false } - val anyRealIpBlocked = - !log!!.responseIps.split(",").none { Utilities.isUnspecifiedIp(it.trim()) } - val hasBlocklist = log!!.blockLists.isNotEmpty() - return anyRealIpBlocked || hasBlocklist + return log!!.upstreamBlock || log!!.blockLists.isNotEmpty() } private fun showBlocklistChip() { diff --git a/app/src/main/java/com/celzero/bravedns/database/DnsLog.kt b/app/src/main/java/com/celzero/bravedns/database/DnsLog.kt index d69877796..40dd7e41e 100644 --- a/app/src/main/java/com/celzero/bravedns/database/DnsLog.kt +++ b/app/src/main/java/com/celzero/bravedns/database/DnsLog.kt @@ -56,6 +56,7 @@ class DnsLog { var responseIps: String = "" var resolverId: String = "" var msg: String = "" + var upstreamBlock: Boolean = false override fun equals(other: Any?): Boolean { if (other !is DnsLog) return false diff --git a/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt b/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt index 619ca6fcb..ec8cbe163 100644 --- a/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt +++ b/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt @@ -31,7 +31,7 @@ import com.celzero.bravedns.util.Utilities @Database( entities = [ConnectionTracker::class, DnsLog::class, RethinkLog::class], - version = 6, + version = 7, exportSchema = false ) @TypeConverters(Converters::class) @@ -69,6 +69,7 @@ abstract class LogDatabase : RoomDatabase() { .addMigrations(MIGRATION_3_4) .addMigrations(MIGRATION_4_5) .addMigrations(MIGRATION_5_6) + .addMigrations(MIGRATION_6_7) .build() } @@ -258,6 +259,16 @@ abstract class LogDatabase : RoomDatabase() { db.execSQL("ALTER TABLE DnsLogs ADD COLUMN msg TEXT DEFAULT '' NOT NULL") } } + + private val MIGRATION_6_7: Migration = + object : Migration(6, 7) { + override fun migrate(db: SupportSQLiteDatabase) { + // add a new column upstreamBlock to DNS log table with default as false + db.execSQL( + "ALTER TABLE DnsLogs ADD COLUMN upstreamBlock INTEGER DEFAULT 0 NOT NULL" + ) + } + } } fun checkPoint() { diff --git a/app/src/main/java/com/celzero/bravedns/net/doh/Transaction.kt b/app/src/main/java/com/celzero/bravedns/net/doh/Transaction.kt index 9c8ce8b94..b030ee7ec 100644 --- a/app/src/main/java/com/celzero/bravedns/net/doh/Transaction.kt +++ b/app/src/main/java/com/celzero/bravedns/net/doh/Transaction.kt @@ -35,6 +35,7 @@ class Transaction { var ttl: Long = 0L var transportType: TransportType = TransportType.DOH var msg: String = "" + var upstreamBlock: Boolean = false enum class Status(val id: Long) { START(Backend.Start), diff --git a/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt b/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt index b6fad4ae6..77ffe5258 100644 --- a/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt @@ -84,6 +84,7 @@ internal constructor( transaction.blocklist = summary.blocklists ?: "" transaction.relayName = summary.relayServer ?: "" transaction.msg = summary.msg ?: "" + transaction.upstreamBlock = summary.upstreamBlocks return transaction } @@ -101,6 +102,7 @@ internal constructor( dnsLog.status = transaction.status.name dnsLog.time = transaction.responseCalendar.timeInMillis dnsLog.msg = transaction.msg + dnsLog.upstreamBlock = transaction.upstreamBlock val typeName = ResourceRecordTypes.getTypeName(transaction.type.toInt()) if (typeName == ResourceRecordTypes.UNKNOWN) { dnsLog.typeName = transaction.type.toString() @@ -148,7 +150,8 @@ internal constructor( } // now, there is no empty response, instead -- is added as response from go if ( - transaction.response == EMPTY_RESPONSE && transaction.blocklist.isNotEmpty() + transaction.response == EMPTY_RESPONSE && + (transaction.blocklist.isNotEmpty() || transaction.upstreamBlock) ) { dnsLog.isBlocked = true } From fcca442241ecc5c2383261ba91eed707e8e490f1 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:40:02 +0530 Subject: [PATCH 48/81] disable rinr if active network is disabled --- .../celzero/bravedns/ui/activity/TunnelSettingsActivity.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt index e6f313f4f..2a838446c 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt @@ -91,6 +91,10 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin _: CompoundButton, b: Boolean -> persistentState.useMultipleNetworks = b + if (!b && persistentState.routeRethinkInRethink) { + persistentState.routeRethinkInRethink = false + displayRethinkInRethinkUi() + } } b.settingsRInRRl.setOnClickListener { From 43e97c393ba7b74648f722c48f6534e7724e6c3f Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:40:59 +0530 Subject: [PATCH 49/81] set negative text color for 0 apps in the wg screen. --- .../com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt index 743386c05..8976228eb 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt @@ -287,8 +287,9 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { mappingViewModel.getAppCountById(id).observe(this) { if (it == 0) { b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentBad)) + } else { + b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentGood)) } - b.applicationsBtn.setTextColor(UIUtils.fetchColor(this, R.attr.accentGood)) b.applicationsBtn.text = getString(R.string.add_remove_apps, it.toString()) } } From 0336aba7be16d421a7c27ee56b776e2dfc77d2f3 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:42:36 +0530 Subject: [PATCH 50/81] change URL endpoint to 'max' instead of 'sky' during URL copy --- .../bravedns/ui/bottomsheet/LocalBlocklistsBottomSheet.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/LocalBlocklistsBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/LocalBlocklistsBottomSheet.kt index a9c8c30aa..15a709b8e 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/LocalBlocklistsBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/LocalBlocklistsBottomSheet.kt @@ -382,7 +382,7 @@ class LocalBlocklistsBottomSheet : BottomSheetDialogFragment() { b.lbbsConfigure.setOnClickListener { invokeRethinkActivity() } b.lbbsCopy.setOnClickListener { - val url = Constants.RETHINK_BASE_URL_SKY + persistentState.localBlocklistStamp + val url = Constants.RETHINK_BASE_URL_MAX + persistentState.localBlocklistStamp clipboardCopy( requireContext(), url, From 6b9697f8b495033a0c459478f7991a876b950f87 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:43:20 +0530 Subject: [PATCH 51/81] ui: rearrange DNS screen settings --- .../ui/fragment/DnsSettingsFragment.kt | 10 +- .../res/layout/fragment_dns_configure.xml | 144 +++++++++--------- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt index 6bc8a69dc..d712261f3 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt @@ -44,12 +44,11 @@ import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.UIUtils.fetchColor import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.util.Utilities.isPlayStoreFlavour +import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.koin.android.ext.android.get import org.koin.android.ext.android.inject -import java.util.concurrent.TimeUnit class DnsSettingsFragment : Fragment(R.layout.fragment_dns_configure), @@ -152,7 +151,8 @@ class DnsSettingsFragment : private fun updateConnectedStatus(connectedDns: String) { if (WireguardManager.oneWireGuardEnabled()) { - b.connectedStatusTitleUrl.text = resources.getString(R.string.configure_dns_connected_dns_proxy_status) + b.connectedStatusTitleUrl.text = + resources.getString(R.string.configure_dns_connected_dns_proxy_status) b.connectedStatusTitle.text = resources.getString(R.string.lbl_wireguard) disableAllDns() b.wireguardRb.isEnabled = true @@ -352,9 +352,7 @@ class DnsSettingsFragment : persistentState.enableDnsCache = b } - b.dcProxyDnsSwitch.setOnCheckedChangeListener { _, b -> - persistentState.proxyDns = !b - } + b.dcProxyDnsSwitch.setOnCheckedChangeListener { _, b -> persistentState.proxyDns = !b } b.dcProxyDnsRl.setOnClickListener { b.dcProxyDnsSwitch.isChecked = !b.dcProxyDnsSwitch.isChecked diff --git a/app/src/main/res/layout/fragment_dns_configure.xml b/app/src/main/res/layout/fragment_dns_configure.xml index 5eabd011f..e1d20dff4 100644 --- a/app/src/main/res/layout/fragment_dns_configure.xml +++ b/app/src/main/res/layout/fragment_dns_configure.xml @@ -276,7 +276,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" - android:paddingTop="5dp" + android:paddingTop="15dp" android:paddingBottom="15dp"> - - - - - - - - - - + android:src="@drawable/ic_update" /> - + android:src="@drawable/ic_blocklist_update_check" /> + + + + + + + + + + + + android:src="@drawable/ic_fav_icon" /> + + android:src="@drawable/ic_auto_start" /> - Date: Thu, 28 Mar 2024 22:44:20 +0530 Subject: [PATCH 52/81] do not navigate to the rethink configure screen when wg DNS is active --- .../com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index 5996597b9..556a2817f 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -903,7 +903,7 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { return } - if (isRethinkDnsActive()) { + if (canStartRethinkActivity()) { // no need to pass value in intent, as default load to Rethink remote startActivity(ScreenType.RETHINK, screenToLoad) return @@ -913,9 +913,9 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { return } - private fun isRethinkDnsActive(): Boolean { + private fun canStartRethinkActivity(): Boolean { val dns = appConfig.getDnsType() - return dns.isRethinkRemote() + return dns.isRethinkRemote() && !WireguardManager.oneWireGuardEnabled() } private fun showPrivateDnsDialog() { From 4e12404a0647a10a058dfff0b68d233df480321e Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:44:37 +0530 Subject: [PATCH 53/81] retry on orbot failure --- .../com/celzero/bravedns/util/OrbotHelper.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt index 26c785b19..cba0bf161 100644 --- a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt +++ b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt @@ -41,14 +41,12 @@ import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.service.ProxyManager import com.celzero.bravedns.ui.HomeScreenActivity import com.celzero.bravedns.util.Constants.Companion.HTTP_PROXY_PORT -import com.celzero.bravedns.util.Constants.Companion.SOCKS_DEFAULT_IP import com.celzero.bravedns.util.Constants.Companion.SOCKS_DEFAULT_PORT import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_VPN import com.celzero.bravedns.util.Utilities.getActivityPendingIntent import com.celzero.bravedns.util.Utilities.getBroadcastPendingIntent import com.celzero.bravedns.util.Utilities.isAtleastO import com.celzero.bravedns.util.Utilities.isAtleastT -import com.celzero.bravedns.util.Utilities.isAtleastU import com.celzero.bravedns.util.Utilities.isFdroidFlavour import com.celzero.bravedns.util.Utilities.isPlayStoreFlavour import com.celzero.bravedns.util.Utilities.isValidPort @@ -192,7 +190,10 @@ class OrbotHelper( selectedProxyType = type val intent = getOrbotStartIntent() isResponseReceivedFromOrbot = false - ContextCompat.registerReceiver(context, orbotStatusReceiver, IntentFilter(ACTION_STATUS), + ContextCompat.registerReceiver( + context, + orbotStatusReceiver, + IntentFilter(ACTION_STATUS), ContextCompat.RECEIVER_EXPORTED ) context.sendBroadcast(intent) @@ -222,18 +223,22 @@ class OrbotHelper( when (status) { STATUS_ON -> { + Log.i(LOG_TAG_VPN, "Orbot is ON, update the proxy data") isResponseReceivedFromOrbot = true updateOrbotProxyData(intent) setOrbotMode() } STATUS_OFF -> { - stopOrbot(isInteractive = false) + Log.i(LOG_TAG_VPN, "Orbot is OFF, retry or stop the Orbot") + io { waitForOrbot() } context.unregisterReceiver(this) } STATUS_STARTING -> { + Log.i(LOG_TAG_VPN, "Orbot is STARTING, update the proxy data") updateOrbotProxyData(intent) } STATUS_STOPPING -> { + Log.i(LOG_TAG_VPN, "Orbot is STOPPING, stop the Proxy") updateOrbotProxyData(intent) stopOrbot(isInteractive = false) } @@ -259,7 +264,7 @@ class OrbotHelper( appConfig.removeAllProxies() persistentState.orbotConnectionStatus.postValue(false) context.sendBroadcast(getOrbotStopIntent()) - if (DEBUG) Log.d(LOG_TAG_VPN, "stop orbot, remove from proxy") + Log.i(LOG_TAG_VPN, "stop orbot, remove from proxy") } /** @@ -441,7 +446,6 @@ class OrbotHelper( ) } - /** * Create a ScheduledExecutorService which will be executed after 30 sec of Orbot status * initiation. @@ -466,7 +470,10 @@ class OrbotHelper( Log.i(LOG_TAG_VPN, "retrying orbot start") startOrbot(selectedProxyType) } else { - uiCtx { stopOrbot(isInteractive = false) } + uiCtx { + stopOrbot(isInteractive = false) + context.unregisterReceiver(orbotStatusReceiver) + } } } } From e53ffd8a29ee1aa1bc2a541a43ba18766a1c4bcd Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:45:32 +0530 Subject: [PATCH 54/81] improvements in connection monitor for v055d --- .../bravedns/service/ConnectionMonitor.kt | 63 +++++++++---------- .../com/celzero/bravedns/util/Utilities.kt | 4 +- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt index df3ff088e..621a16744 100644 --- a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt +++ b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt @@ -48,11 +48,7 @@ import java.net.Socket import java.util.concurrent.TimeUnit import kotlin.math.max import kotlin.math.min -import kotlinx.coroutines.async -import kotlinx.coroutines.cancelChildren -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.selects.select import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -510,7 +506,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : var hasDefaultRoute4 = false var hasDefaultRoute6 = false lp.routes.forEach rloop@{ - // ref: androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/net/RouteInfo.java#328 + // ref: + // androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/net/RouteInfo.java#328 hasDefaultRoute4 = hasDefaultRoute4 || (it.isDefaultRoute && it.destination.address is Inet4Address) @@ -573,8 +570,11 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : currentNetworks: LinkedHashSet, newNetworks: LinkedHashSet ): Boolean { - val cn = currentNetworks.map { it.network }.toSet() - val nn = newNetworks.map { it.network }.toSet() + if (currentNetworks.size != newNetworks.size) { + return true + } + val cn = currentNetworks.map { it.network.networkHandle }.toHashSet() + val nn = newNetworks.map { it.network.networkHandle }.toHashSet() return Sets.symmetricDifference(cn, nn).isNotEmpty() } @@ -675,29 +675,22 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : nw: Network?, isActive: Boolean = false ): Boolean = runBlocking { - coroutineScope { - // select the first reachable IP / domain and return true if any of them is - // reachable - select { - probes.forEach { ip -> - async { - var ok = false - if (isActive) { - ok = isReachable(ip) - } - if (!ok) { - ok = isReachableTcpUdp(nw, ip) - } - ok - } - .onAwait { it } - } - } - .also { coroutineContext.cancelChildren() } + var ok = false + probes.forEach { ip -> + if (isActive) { + ok = isReachable(ip) + } + if (!ok) { + ok = isReachableTcpUdp(nw, ip) + } + if (ok) { + return@forEach // break + } } + return@runBlocking ok } - private fun isReachableTcpUdp(nw: Network?, host: String): Boolean { + private suspend fun isReachableTcpUdp(nw: Network?, host: String): Boolean { try { // https://developer.android.com/reference/android/net/Network#bindSocket(java.net.Socket) TrafficStats.setThreadStatsTag(Thread.currentThread().id.toIntOrDefault()) @@ -712,14 +705,14 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : return false } - private fun isReachable(host: String): Boolean { + private suspend fun isReachable(host: String): Boolean { // The 'isReachable()' function sends an ICMP echo request to the target host. In the // event of the 'Rethink within Rethink' option being used, ICMP checks will fail. // Ideally, there is no need to perform ICMP checks. However, 'Rethink within // Rethink' is not supplied to this handler, these checks will be carried out but will // result in failure. try { - val onesec = 1000 // ms + val timeout = 500 // ms // https://developer.android.com/reference/android/net/Network#bindSocket(java.net.Socket) TrafficStats.setThreadStatsTag(Thread.currentThread().id.toIntOrDefault()) // https://developer.android.com/reference/java/net/InetAddress#isReachable(int) @@ -727,7 +720,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : // isReachable(network-interface, ttl, timeout) cannot be used here since // "network-interface" is a java-construct while "Network" is an android-construct // InetAddress.getByName() will bind the socket to the default active network. - val yes = InetAddress.getByName(host).isReachable(onesec) + val yes = InetAddress.getByName(host).isReachable(timeout) if (DEBUG) Log.d(LOG_TAG_CONNECTION, "$host isReachable on network: $yes") return yes @@ -738,8 +731,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : } // https://android.googlesource.com/platform/prebuilts/fullsdk/sources/android-30/+/refs/heads/androidx-benchmark-release/java/net/Inet6AddressImpl.java#217 - private fun tcp80(nw: Network?, host: String): Boolean { - val onesec = 1000 // ms + private suspend fun tcp80(nw: Network?, host: String): Boolean { + val timeout = 500 // ms val port80 = 80 // port var socket: Socket? = null try { @@ -747,7 +740,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : val s = InetSocketAddress(host, port80) socket = Socket() nw?.bindSocket(socket) - socket.connect(s, onesec) + socket.connect(s, timeout) val c = socket.isConnected val b = socket.isBound if (DEBUG) @@ -770,7 +763,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : } } - private fun tcp53(nw: Network?, host: String): Boolean { + private suspend fun tcp53(nw: Network?, host: String): Boolean { val port53 = 53 // port var socket: Socket? = null @@ -799,7 +792,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : return false } - private fun udp53(nw: Network?, host: String): Boolean { + private suspend fun udp53(nw: Network?, host: String): Boolean { val port53 = 53 // port var socket: DatagramSocket? = null diff --git a/app/src/main/java/com/celzero/bravedns/util/Utilities.kt b/app/src/main/java/com/celzero/bravedns/util/Utilities.kt index 2f004dece..c330bc92d 100644 --- a/app/src/main/java/com/celzero/bravedns/util/Utilities.kt +++ b/app/src/main/java/com/celzero/bravedns/util/Utilities.kt @@ -782,6 +782,8 @@ object Utilities { } fun isNetworkSame(n1: Network?, n2: Network?): Boolean { - return n1?.networkHandle == n2?.networkHandle + if (n1 == null || n2 == null) return false + + return n1.networkHandle == n2.networkHandle } } From 7a398966da07c3e6f72eb5cc43ac9727f8cf0390 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Thu, 28 Mar 2024 22:47:03 +0530 Subject: [PATCH 55/81] ui: update the correct protocol used to VPNController --- .../celzero/bravedns/service/VpnController.kt | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/VpnController.kt b/app/src/main/java/com/celzero/bravedns/service/VpnController.kt index 84dd3a83e..853cb9708 100644 --- a/app/src/main/java/com/celzero/bravedns/service/VpnController.kt +++ b/app/src/main/java/com/celzero/bravedns/service/VpnController.kt @@ -24,6 +24,7 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData import backend.RDNS import com.celzero.bravedns.R +import com.celzero.bravedns.service.BraveVPNService.Companion.FAIL_OPEN_ON_NO_NETWORK import com.celzero.bravedns.util.Constants.Companion.INVALID_UID import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_VPN import com.celzero.bravedns.util.Utilities @@ -43,6 +44,8 @@ object VpnController : KoinComponent { private var connectionState: BraveVPNService.State? = null private val persistentState by inject() private var states: Channel? = null + private var protocol: Pair = Pair(false, false) + var controllerScope: CoroutineScope? = null private set @@ -207,27 +210,47 @@ object VpnController : KoinComponent { return braveVpnService?.getProxyStatusById(id) } + fun getSupportedIpVersion(id: String): Pair { + return braveVpnService?.getSupportedIpVersion(id) ?: Pair(false, false) + } + + fun isSplitTunnelProxy(id: String, pair: Pair): Boolean { + return braveVpnService?.isSplitTunnelProxy(id, pair) ?: false + } + suspend fun syncP50Latency(id: String) { braveVpnService?.syncP50Latency(id) } fun protocols(): String { - val ipv4Size = braveVpnService?.underlyingNetworks?.ipv4Net?.size ?: -1 - val ipv6Size = braveVpnService?.underlyingNetworks?.ipv6Net?.size ?: -1 - Log.d(LOG_TAG_VPN, "protocols - ipv4Size: $ipv4Size, ipv6Size: $ipv6Size") - return if (ipv4Size >= 1 && ipv6Size >= 1) { + val ipv4 = protocol.first + val ipv6 = protocol.second + Log.d(LOG_TAG_VPN, "protocols - ipv4: $ipv4, ipv6: $ipv6") + return if (ipv4 && ipv6) { "IPv4, IPv6" - } else if (ipv6Size >= 1) { + } else if (ipv6) { "IPv6" - } else if (ipv4Size >= 1) { + } else if (ipv4) { "IPv4" } else { - // if there are zero ipv4 and ipv6 networks, then we are failing open - // see: BraveVpnService#establishVpn - "IPv4, IPv6" + // if both are false, then return based on the FAIL_OPEN_ON_NO_NETWORK value + if (FAIL_OPEN_ON_NO_NETWORK) { + "IPv4, IPv6" + } else { + "" + } } } + fun updateProtocol(proto: Pair) { + if (!proto.first && !proto.second) { + Log.i(LOG_TAG_VPN, "both v4 and v6 false, setting $FAIL_OPEN_ON_NO_NETWORK") + protocol = Pair(FAIL_OPEN_ON_NO_NETWORK, FAIL_OPEN_ON_NO_NETWORK) + return + } + protocol = proto + } + fun mtu(): Int { return braveVpnService?.underlyingNetworks?.minMtu ?: BraveVPNService.VPN_INTERFACE_MTU } @@ -267,10 +290,6 @@ object VpnController : KoinComponent { braveVpnService?.refreshProxies() } - fun updateWireGuardConfig() { - braveVpnService?.updateWireGuardConfig() - } - fun closeConnectionsIfNeeded(uid: Int = INVALID_UID) { braveVpnService?.closeConnectionsIfNeeded(uid) } From 370995a9b70c6b388a7fab2164bd21e07c172d4a Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Fri, 29 Mar 2024 13:46:56 +0530 Subject: [PATCH 56/81] improvements in VPN service and tunnel for v055d - Handle overlay networks while adding routes to VPN. - Improve route/MTU change checks on network connection. - Disable allowBypass in proxy mode. - Improve handling of system DNS servers. - Bug fix: Correct determination of transport type during flow(). - Add new callback on proxy add/remove. - Show toast on transport failures. - Remove addition of block-free transport other than Rethink's transport. - Convert stamp to base32 before creating it. - Bug fix: Correct addition of SOCKS5 and HTTP proxy. - Use fallback DNS instead of loopback in RinR mode. - Add new route IP, supporting IP versions by proxy check. --- .../celzero/bravedns/net/go/GoVpnAdapter.kt | 368 +++--- .../bravedns/service/BraveVPNService.kt | 1011 ++++++++++------- 2 files changed, 820 insertions(+), 559 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt index dea67de71..467a036b6 100644 --- a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt +++ b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt @@ -25,8 +25,11 @@ import backend.Backend import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.data.AppConfig +import com.celzero.bravedns.data.AppConfig.Companion.LOOPBACK_DNS +import com.celzero.bravedns.data.AppConfig.Companion.RINR_FALLBACK_DNS import com.celzero.bravedns.data.AppConfig.TunnelOptions import com.celzero.bravedns.database.ProxyEndpoint +import com.celzero.bravedns.service.BraveVPNService import com.celzero.bravedns.service.PersistentState import com.celzero.bravedns.service.ProxyManager import com.celzero.bravedns.service.ProxyManager.ID_WG_BASE @@ -39,8 +42,9 @@ import com.celzero.bravedns.util.Constants.Companion.ONDEVICE_BLOCKLIST_FILE_TAG import com.celzero.bravedns.util.Constants.Companion.REMOTE_BLOCKLIST_DOWNLOAD_FOLDER_NAME import com.celzero.bravedns.util.Constants.Companion.RETHINKDNS_DOMAIN import com.celzero.bravedns.util.Constants.Companion.RETHINK_BASE_URL_SKY +import com.celzero.bravedns.util.Constants.Companion.UNSPECIFIED_IP_IPV4 +import com.celzero.bravedns.util.Constants.Companion.UNSPECIFIED_IP_IPV6 import com.celzero.bravedns.util.InternetProtocol -import com.celzero.bravedns.util.KnownPorts import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_VPN import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.util.Utilities.blocklistDir @@ -49,12 +53,12 @@ import com.celzero.bravedns.util.Utilities.showToastUiCentered import com.celzero.bravedns.wireguard.Config import intra.Intra import intra.Tunnel +import java.net.URI import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import java.net.URI /** * This is a VpnAdapter that captures all traffic and routes it through a go-tun2socks instance with @@ -108,9 +112,11 @@ class GoVpnAdapter : KoinComponent { setSocks5TunnelModeIfNeeded(opts.tunProxyMode) setHttpProxyIfNeeded(opts.tunProxyMode) setPcapMode(appConfig.getPcapFilePath()) + // TODO: ideally the values required for transport, alg and rdns should be set in the + // opts itself. + setRDNS() addTransport() setDnsAlg() - setRDNS() } fun setPcapMode(pcapFilePath: String) { @@ -155,23 +161,18 @@ class GoVpnAdapter : KoinComponent { // because of the way the alg is implemented in the go code. when (appConfig.getDnsType()) { AppConfig.DnsType.DOH -> { - addDohTransport(Backend.BlockFree) addDohTransport(Backend.Preferred) } AppConfig.DnsType.DOT -> { - addDotTransport(Backend.BlockFree) addDotTransport(Backend.Preferred) } AppConfig.DnsType.ODOH -> { - addOdohTransport(Backend.BlockFree) addOdohTransport(Backend.Preferred) } AppConfig.DnsType.DNSCRYPT -> { - addDnscryptTransport(Backend.BlockFree) addDnscryptTransport(Backend.Preferred) } AppConfig.DnsType.DNS_PROXY -> { - addDnsProxyTransport(Backend.BlockFree) addDnsProxyTransport(Backend.Preferred) } AppConfig.DnsType.SYSTEM_DNS -> { @@ -202,9 +203,10 @@ class GoVpnAdapter : KoinComponent { } private suspend fun addDohTransport(id: String) { + var url: String? = null try { val doh = appConfig.getDOHDetails() - var url = doh?.dohURL + url = doh?.dohURL // change the url from https to http if the isSecure is false if (doh?.isSecure == false) { if (DEBUG) Log.d(LOG_TAG_VPN, "changing url from https to http for $url") @@ -218,13 +220,15 @@ class GoVpnAdapter : KoinComponent { } catch (e: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: doh failure", e) getResolver()?.remove(id) + showDnsFailureToast(url ?: "") } } private suspend fun addDotTransport(id: String) { + var url: String? = null try { val dot = appConfig.getDOTDetails() - var url = dot?.url + url = dot?.url if (dot?.isSecure == true && url?.startsWith("tls") == false) { if (DEBUG) Log.d(LOG_TAG_VPN, "adding tls to url for $url") // add tls to the url if isSecure is true and the url does not start with tls @@ -238,14 +242,16 @@ class GoVpnAdapter : KoinComponent { } catch (e: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: dot failure", e) getResolver()?.remove(id) + showDnsFailureToast(url ?: "") } } private suspend fun addOdohTransport(id: String) { + var resolver: String? = null try { val odoh = appConfig.getODoHDetails() val proxy = odoh?.proxy - val resolver = odoh?.resolver + resolver = odoh?.resolver val proxyIps = "" // add replaces the existing transport with the same id if successful // so no need to remove the transport before adding @@ -254,6 +260,7 @@ class GoVpnAdapter : KoinComponent { } catch (e: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: odoh failure", e) getResolver()?.remove(id) + showDnsFailureToast(resolver ?: "") } } @@ -265,13 +272,13 @@ class GoVpnAdapter : KoinComponent { // so no need to remove the transport before adding Intra.addDNSCryptTransport(tunnel, id, url) Log.i(LOG_TAG_VPN, "new dnscrypt: $id (${dc.dnsCryptName}), url: $url") + // setDnscryptRelaysIfAny() is expected to catch exceptions + setDnscryptRelaysIfAny() } catch (e: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: dns crypt failure for $id", e) getResolver()?.remove(id) showDnscryptConnectionFailureToast() } - // setDnscryptRelaysIfAny() is expected to catch exceptions - setDnscryptRelaysIfAny() } private suspend fun addDnsProxyTransport(id: String) { @@ -291,18 +298,6 @@ class GoVpnAdapter : KoinComponent { } } - private suspend fun addSystemDnsAsBlockfreeTransport(dns: String?) { - if (dns == null) { - Log.w(LOG_TAG_VPN, "null sys-dns for blockfree transport") - } - try { - Intra.addDNSProxy(tunnel, Backend.BlockFree, dns, KnownPorts.DNS_PORT.toString()) - Log.i(LOG_TAG_VPN, "new system dns ip: ${dns}, port: ${KnownPorts.DNS_PORT}") - } catch (e: Exception) { - Log.e(LOG_TAG_VPN, "connect-tunnel: system dns failure", e) - } - } - private suspend fun addRdnsTransport(id: String, url: String) { try { val ips: String = getIpString(context, url) @@ -316,6 +311,8 @@ class GoVpnAdapter : KoinComponent { } } catch (e: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: rdns creation failure", e) + getResolver()?.remove(id) + showDnsFailureToast(url) } } @@ -336,6 +333,19 @@ class GoVpnAdapter : KoinComponent { } else { // no-op, pass-through } + val stamp = getRdnsStamp(url) + return if (url.contains(MAX_ENDPOINT)) { + // if the stamp is empty or "dns-query", then remove it + if (stamp.isEmpty() || stamp == default) { + return "$tls$MAX_ENDPOINT.$RETHINKDNS_DOMAIN" + } + "$tls$stamp.$MAX_ENDPOINT.$RETHINKDNS_DOMAIN" + } else { + "$RETHINK_BASE_URL_SKY$stamp" + } + } + + private fun getRdnsStamp(url: String): String { val s = url.split(RETHINKDNS_DOMAIN)[1] val stamp = if (s.startsWith("/")) { @@ -343,14 +353,26 @@ class GoVpnAdapter : KoinComponent { } else { s } - return if (url.contains(MAX_ENDPOINT)) { - // if the stamp is empty or "dns-query", then remove it - if (stamp.isEmpty() || stamp == default) return "$tls$MAX_ENDPOINT.$RETHINKDNS_DOMAIN" + return getBase32Stamp(stamp) + } - "$tls$stamp.$MAX_ENDPOINT.$RETHINKDNS_DOMAIN" - } else { - "$RETHINK_BASE_URL_SKY$stamp" + private fun getBase32Stamp(stamp: String): String { + // in v055a, the stamps are generated either by base32 or base64 + // if the stamp is base64, then convert it to base32 + // if the stamp is base32, then return the stamp + // as of now, only way to find the base 32 stamp is to get the flags and convert it to stamp + // with eb32 encoding + // sample stamp https://max.rethinkdns.com/1:-AcHAL6d_5-Yxf7zMQAKAAAI which is base64 + // output stamp 1-7adqoaf6tx7z7ggf73ztcaakaaaaq which is base32 + var b32Stamp: String? = null + try { + val r = getRDNS(RethinkBlocklistManager.RethinkBlocklistType.REMOTE) + val flags = r?.stampToFlags(stamp) + b32Stamp = r?.flagsToStamp(flags, Backend.EB32) + } catch (e: Exception) { + Log.w(LOG_TAG_VPN, "err get base32 stamp: ${e.message}") } + return b32Stamp ?: stamp } private fun setTunnelMode(tunnelOptions: TunnelOptions) { @@ -377,22 +399,20 @@ class GoVpnAdapter : KoinComponent { } } - fun setRDNS() { + suspend fun setRDNS() { // Set brave dns to tunnel - Local/Remote if (DEBUG) Log.d(LOG_TAG_VPN, "set brave dns to tunnel (local/remote)") // enable local blocklist if enabled - io { - if (persistentState.blocklistEnabled) { - setRDNSLocal() - } else { - // remove local blocklist, if any - getRDNSResolver()?.setRdnsLocal(null, null, null, null) - } - - // always set the remote blocklist - setRDNSRemote() + if (persistentState.blocklistEnabled) { + setRDNSLocal() + } else { + // remove local blocklist, if any + getRDNSResolver()?.setRdnsLocal(null, null, null, null) } + + // always set the remote blocklist + setRDNSRemote() } private fun setRDNSRemote() { @@ -422,12 +442,13 @@ class GoVpnAdapter : KoinComponent { routes.split(",").forEach { if (it.isBlank()) return@forEach - Log.i(LOG_TAG_VPN, "new dnscrypt: $it") + Log.i(LOG_TAG_VPN, "new dnscrypt relay: $it") try { - Intra.addDNSCryptRelay(tunnel, it) + Intra.addDNSCryptRelay(tunnel, it) // entire url is the id } catch (ex: Exception) { Log.e(LOG_TAG_VPN, "connect-tunnel: dnscrypt failure", ex) appConfig.removeDnscryptRelay(it) + getResolver()?.remove(it) } } } @@ -492,6 +513,18 @@ class GoVpnAdapter : KoinComponent { } } + private fun showDnsFailureToast(url: String) { + ui { + val msg = + context.getString( + R.string.two_argument_colon, + url, + context.getString(R.string.dns_proxy_connection_failure) + ) + showToastUiCentered(context.applicationContext, msg, Toast.LENGTH_LONG) + } + } + private fun showWireguardFailureToast(message: String) { ui { showToastUiCentered(context.applicationContext, message, Toast.LENGTH_LONG) } } @@ -506,35 +539,18 @@ class GoVpnAdapter : KoinComponent { } } - suspend fun setWireguardTunnelModeIfNeeded(tunProxyMode: AppConfig.TunProxyMode): Boolean { - if (!tunProxyMode.isTunProxyWireguard()) return false + private suspend fun setWireguardTunnelModeIfNeeded(tunProxyMode: AppConfig.TunProxyMode) { + if (!tunProxyMode.isTunProxyWireguard()) return val wgConfigs: List = WireguardManager.getEnabledConfigs() if (wgConfigs.isEmpty()) { Log.i(LOG_TAG_VPN, "no active wireguard configs found") - return false + return } wgConfigs.forEach { - val wgUserSpaceString = it.toWgUserspaceString() val id = ID_WG_BASE + it.getId() - val isDnsNeeded = WireguardManager.getOneWireGuardProxyId() == it.getId() - try { - if (DEBUG) Log.d(LOG_TAG_VPN, "add wireguard proxy with id: $id") - - getProxies()?.addProxy(id, wgUserSpaceString) - if (isDnsNeeded) { - setWireGuardDns(id) - } - } catch (e: Exception) { - WireguardManager.disableConfig(id) - showWireguardFailureToast( - e.message ?: context.getString(R.string.wireguard_connection_error) - ) - Log.e(LOG_TAG_VPN, "connect-tunnel: could not start wireguard", e) - return false - } + addWgProxy(id) } - return true } private fun setWireGuardDns(id: String) { @@ -544,9 +560,11 @@ class GoVpnAdapter : KoinComponent { Log.w(LOG_TAG_VPN, "wireguard proxy not found for id: $id") return } - Intra.addProxyDNS(tunnel, p) + Intra.addProxyDNS(tunnel, p) // dns transport has same id as the proxy (p) } catch (e: Exception) { - Log.e(LOG_TAG_VPN, "connect-tunnel: dns crypt failure", e) + Log.e(LOG_TAG_VPN, "wireguard dns failure", e) + getResolver()?.remove(id) + showDnsFailureToast(id) } } @@ -555,32 +573,28 @@ class GoVpnAdapter : KoinComponent { setHttpProxyIfNeeded(tunProxyMode) } - private fun setSocks5TunnelModeIfNeeded(tunProxyMode: AppConfig.TunProxyMode) { - if (!tunProxyMode.isTunProxySocks5() && !tunProxyMode.isTunProxyOrbot()) return + private suspend fun setSocks5TunnelModeIfNeeded(tunProxyMode: AppConfig.TunProxyMode) { + val socksEnabled = AppConfig.ProxyType.of(persistentState.proxyType).isSocks5Enabled() + if (!socksEnabled) return - io { - val socks5: ProxyEndpoint? = - if ( - tunProxyMode.isTunProxyOrbot() && - AppConfig.ProxyType.of(persistentState.proxyType).isSocks5Enabled() - ) { - appConfig.getConnectedOrbotProxy() - } else { - appConfig.getSocks5ProxyDetails() - } - if (socks5 == null) { - Log.w(LOG_TAG_VPN, "could not fetch socks5 details for proxyMode: $tunProxyMode") - return@io + val socks5: ProxyEndpoint? = + if (tunProxyMode.isTunProxyOrbot()) { + appConfig.getConnectedOrbotProxy() + } else { + appConfig.getSocks5ProxyDetails() } - setSocks5Proxy( - tunProxyMode, - socks5.userName, - socks5.password, - socks5.proxyIP, - socks5.proxyPort - ) - Log.i(LOG_TAG_VPN, "Socks5 mode set: " + socks5.proxyIP + "," + socks5.proxyPort) + if (socks5 == null) { + Log.w(LOG_TAG_VPN, "could not fetch socks5 details for proxyMode: $tunProxyMode") + return } + setSocks5Proxy( + tunProxyMode, + socks5.userName, + socks5.password, + socks5.proxyIP, + socks5.proxyPort + ) + Log.i(LOG_TAG_VPN, "Socks5 mode set: " + socks5.proxyIP + "," + socks5.proxyPort) } fun getProxyStatusById(id: String): Long? { @@ -594,7 +608,7 @@ class GoVpnAdapter : KoinComponent { } } - fun getProxyById(id: String): backend.Proxy? { + private fun getProxyById(id: String): backend.Proxy? { return try { getProxies()?.getProxy(id) } catch (ignored: Exception) { @@ -606,18 +620,20 @@ class GoVpnAdapter : KoinComponent { private suspend fun setHttpProxyIfNeeded(tunProxyMode: AppConfig.TunProxyMode) { if (!AppConfig.ProxyType.of(appConfig.getProxyType()).isProxyTypeHasHttp()) return - val endpoint = appConfig.getHttpProxyDetails() try { + val endpoint: ProxyEndpoint val id = if (tunProxyMode.isTunProxyOrbot()) { + endpoint = appConfig.getOrbotHttpEndpoint() ProxyManager.ID_ORBOT_BASE } else { + endpoint = appConfig.getHttpProxyDetails() ProxyManager.ID_HTTP_BASE } - val httpProxyUrl = endpoint?.proxyIP ?: return + val httpProxyUrl = endpoint.proxyIP ?: return - getProxies()?.addProxy(id, httpProxyUrl) Log.i(LOG_TAG_VPN, "Http mode set with url: $httpProxyUrl") + getProxies()?.addProxy(id, httpProxyUrl) } catch (e: Exception) { if (tunProxyMode.isTunProxyOrbot()) { appConfig.removeProxy(AppConfig.ProxyType.HTTP, AppConfig.ProxyProvider.ORBOT) @@ -658,13 +674,17 @@ class GoVpnAdapter : KoinComponent { Log.e(LOG_TAG_VPN, "invalid wireguard proxy id: $id") return } + val wgConfig = WireguardManager.getConfigById(proxyId) + val withDNS = WireguardManager.getOneWireGuardProxyId() == proxyId val wgUserSpaceString = wgConfig?.toWgUserspaceString() getProxies()?.addProxy(id, wgUserSpaceString) - Log.i(LOG_TAG_VPN, "add wireguard proxy with id: $id") + if (withDNS) setWireGuardDns(id) + Log.i(LOG_TAG_VPN, "add wireguard proxy with $id; dns? $withDNS") } catch (e: Exception) { Log.e(LOG_TAG_VPN, "error adding wireguard proxy: ${e.message}", e) - WireguardManager.disableConfig(id) + // do not auto remove failed wg proxy, let the user decide via UI + // WireguardManager.disableConfig(id) showWireguardFailureToast( e.message ?: context.getString(R.string.wireguard_connection_error) ) @@ -700,7 +720,6 @@ class GoVpnAdapter : KoinComponent { val transport = getResolver()?.get(id) val tid = transport?.id() val status = transport?.status() - if (DEBUG) Log.d(LOG_TAG_VPN, "dns status($id): $tid, $status") // some special transports like blockfree, preferred,alg etc are handled specially. // in those cases, if the transport id is not there, it will serve the default transport // so return null in those cases @@ -709,17 +728,22 @@ class GoVpnAdapter : KoinComponent { } return status } catch (e: Exception) { - Log.e(LOG_TAG_VPN, "err dns status($id): ${e.message}", e) + Log.w(LOG_TAG_VPN, "err dns status($id): ${e.message}") } return null } fun getRDNS(type: RethinkBlocklistManager.RethinkBlocklistType): backend.RDNS? { - return if (type.isLocal()) { - getRDNSResolver()?.rdnsLocal - } else { - getRDNSResolver()?.rdnsRemote + try { + return if (type.isLocal()) { + getRDNSResolver()?.rdnsLocal + } else { + getRDNSResolver()?.rdnsRemote + } + } catch (e: Exception) { + Log.w(LOG_TAG_VPN, "err getRDNS($type): ${e.message}", e) } + return null } // v055, unused @@ -774,11 +798,11 @@ class GoVpnAdapter : KoinComponent { } private fun newDefaultTransport(url: String): intra.DefaultDNS { - val defaultDns = "8.8.4.4:53" + val defaultDns = getLoopbackOrFallbackDns() try { // when the url is empty, set the default transport to 8.8.4.4:53 if (url.isEmpty()) { - Log.i(LOG_TAG_VPN, "set default transport to 8.8.4.4, as url is empty") + Log.i(LOG_TAG_VPN, "set default transport to $defaultDns, as url is empty") return Intra.newDefaultDNS(Backend.DNS53, defaultDns, "") } val ips: String = getIpString(context, url) @@ -796,16 +820,28 @@ class GoVpnAdapter : KoinComponent { } } + private fun getLoopbackOrFallbackDns(): String { + // in rinr loopback mode, the loopback dns is send back to the tunnel this will result in + // infinite loop, so use RINR_FALLBACK_DNS instead of LOOPBACK_DNS + return if (persistentState.routeRethinkInRethink) { + RINR_FALLBACK_DNS + } else { + LOOPBACK_DNS + } + } + fun addDefaultTransport(url: String?) { - val defaultDns = "8.8.4.4:53" + val defaultDns = getLoopbackOrFallbackDns() try { if (!tunnel.isConnected) { Log.i(LOG_TAG_VPN, "Tunnel not connected, skip new default transport") return } - // when the url is empty, set the default transport to 8.8.4.4:53 + // when the url is empty, set the default transport to DEFAULT_DNS_IP + // default transport is always sent to Ipn.Exit in the go code and so dns + // request sent to the default transport will not be looped back into the tunnel if (url.isNullOrEmpty()) { - Log.i(LOG_TAG_VPN, "set default transport to 8.8.4.4, as url is empty") + Log.i(LOG_TAG_VPN, "url empty, set default trans to $defaultDns") Intra.addDefaultTransport(tunnel, Backend.DNS53, defaultDns, "") return } @@ -829,7 +865,8 @@ class GoVpnAdapter : KoinComponent { if (!tunnel.isConnected) { Log.i(LOG_TAG_VPN, "Tunnel NOT connected, skip setting system-dns") } - + // for Rethink within rethink mode, the system dns is system dns is always set to Ipn.Base + // in go and so dns request sent to the system dns will be looped back into the tunnel try { // TODO: system dns may be non existent; see: AppConfig#updateSystemDnsServers // convert list to comma separated string, as Intra expects as csv @@ -842,11 +879,8 @@ class GoVpnAdapter : KoinComponent { Intra.setSystemDNS(tunnel, sysDnsStr) } catch (e: Exception) { // this is not expected to happen Log.e(LOG_TAG_VPN, "set system dns: could not parse system dns", e) - } - - // add appropriate transports to the tunnel, if system dns is enabled - if (appConfig.isSystemDns()) { - addSystemDnsAsBlockfreeTransport(systemDns.firstOrNull()) + // see BraveVpnService#determineSystemDns() + Intra.setSystemDNS(tunnel, LOOPBACK_DNS) } } @@ -899,9 +933,10 @@ class GoVpnAdapter : KoinComponent { fun setRDNSStamp() { try { - if (getRDNSResolver()?.rdnsLocal != null) { + val rl = getRDNS(RethinkBlocklistManager.RethinkBlocklistType.LOCAL) + if (rl != null) { Log.i(LOG_TAG_VPN, "set local stamp: ${persistentState.localBlocklistStamp}") - getRDNSResolver()?.rdnsLocal?.stamp = persistentState.localBlocklistStamp + rl.stamp = persistentState.localBlocklistStamp } else { Log.w(LOG_TAG_VPN, "mode is not local, this should not happen") } @@ -914,12 +949,13 @@ class GoVpnAdapter : KoinComponent { private fun resetLocalBlocklistStampFromTunnel() { try { - if (getRDNSResolver()?.rdnsLocal == null) { + val rl = getRDNS(RethinkBlocklistManager.RethinkBlocklistType.LOCAL) + if (rl == null) { Log.i(LOG_TAG_VPN, "mode is not local, no need to reset local stamp") return } - persistentState.localBlocklistStamp = getRDNSResolver()?.rdnsLocal?.stamp ?: "" + persistentState.localBlocklistStamp = rl.stamp // throws exception if stamp is invalid if (DEBUG) Log.d(LOG_TAG_VPN, "reset local stamp: ${persistentState.localBlocklistStamp}") } catch (e: Exception) { @@ -931,7 +967,8 @@ class GoVpnAdapter : KoinComponent { private fun setRDNSLocal() { try { val stamp: String = persistentState.localBlocklistStamp - Log.i(LOG_TAG_VPN, "local blocklist stamp: $stamp") + val rdns = getRDNSResolver() + Log.i(LOG_TAG_VPN, "local blocklist stamp: $stamp, rdns? ${rdns != null}") val path: String = Utilities.blocklistDownloadBasePath( @@ -939,14 +976,13 @@ class GoVpnAdapter : KoinComponent { Constants.LOCAL_BLOCKLIST_DOWNLOAD_FOLDER_NAME, persistentState.localBlocklistTimestamp ) - getRDNSResolver() - ?.setRdnsLocal( - path + Constants.ONDEVICE_BLOCKLIST_FILE_TD, - path + Constants.ONDEVICE_BLOCKLIST_FILE_RD, - path + Constants.ONDEVICE_BLOCKLIST_FILE_BASIC_CONFIG, - path + ONDEVICE_BLOCKLIST_FILE_TAG - ) - getRDNSResolver()?.rdnsLocal?.stamp = stamp + rdns?.setRdnsLocal( + path + Constants.ONDEVICE_BLOCKLIST_FILE_TD, + path + Constants.ONDEVICE_BLOCKLIST_FILE_RD, + path + Constants.ONDEVICE_BLOCKLIST_FILE_BASIC_CONFIG, + path + ONDEVICE_BLOCKLIST_FILE_TAG + ) + rdns?.rdnsLocal?.stamp = stamp Log.i(LOG_TAG_VPN, "local brave dns object is set with stamp: $stamp") } catch (ex: Exception) { // Set local blocklist enabled to false and reset the timestamp @@ -982,18 +1018,17 @@ class GoVpnAdapter : KoinComponent { } fun syncP50Latency(id: String) { + val tid: String = if (persistentState.enableDnsCache && !id.startsWith(Backend.CT)) { + Backend.CT + id + } else { + id + } try { - val tid = - if (persistentState.enableDnsCache && !id.startsWith(Backend.CT)) { - Backend.CT + id - } else { - id - } val transport = getResolver()?.get(tid) val p50 = transport?.p50() ?: return persistentState.setMedianLatency(p50) } catch (e: Exception) { - Log.e(LOG_TAG_VPN, "err getP50: ${e.message}", e) + Log.w(LOG_TAG_VPN, "err getP50($tid): ${e.message}") } } @@ -1013,7 +1048,7 @@ class GoVpnAdapter : KoinComponent { private fun getRDNSResolver(): backend.DNSResolver? { try { if (!tunnel.isConnected) { - Log.i(LOG_TAG_VPN, "Tunnel NOT connected, skip get resolver") + Log.w(LOG_TAG_VPN, "Tunnel NOT connected, skip get resolver") return null } return tunnel.resolver @@ -1026,7 +1061,7 @@ class GoVpnAdapter : KoinComponent { private fun getProxies(): backend.Proxies? { try { if (!tunnel.isConnected) { - Log.i(LOG_TAG_VPN, "Tunnel NOT connected, skip get proxies") + Log.w(LOG_TAG_VPN, "Tunnel NOT connected, skip get proxies") return null } return tunnel.proxies @@ -1036,8 +1071,67 @@ class GoVpnAdapter : KoinComponent { return null } - private fun io(f: suspend () -> Unit) { - externalScope.launch(Dispatchers.IO) { f() } + fun canRouteIp(wgId: String, ip: String, default: Boolean): Boolean { + return try { + val router = tunnel.proxies.getProxy(wgId).router() + val res = router.contains(ip) + Log.i(LOG_TAG_VPN, "canRouteIp($wgId, $ip), res? $res") + res + } catch (e: Exception) { + Log.e(LOG_TAG_VPN, "err canRouteIp($wgId, $ip): ${e.message}", e) + default + } + } + + fun getSupportedIpVersion(proxyId: String): Pair { + try { + val router = tunnel.proxies.getProxy(proxyId).router() + val has4 = router.iP4() + val has6 = router.iP6() + Log.i(LOG_TAG_VPN, "supported ip version($proxyId): has4? $has4, has6? $has6") + return Pair(has4, has6) + } catch (e: Exception) { + Log.w(LOG_TAG_VPN, "err supported ip version($proxyId): ${e.message}") + } + return Pair(false, false) + } + + fun isSplitTunnelProxy(proxyId: String, pair: Pair): Boolean { + return try { + val router = tunnel.proxies.getProxy(proxyId).router() + // if the router contains 0.0.0.0, then it is not split tunnel for ipv4 + // if the router contains ::, then it is not split tunnel for ipv6 + val res: Boolean = if (pair.first && pair.second) { + // if the pair is true, check for both ipv4 and ipv6 + !router.contains(UNSPECIFIED_IP_IPV4) || !router.contains(UNSPECIFIED_IP_IPV6) + } else if (pair.first) { + !router.contains(UNSPECIFIED_IP_IPV4) + } else if (pair.second) { + !router.contains(UNSPECIFIED_IP_IPV6) + } else { + false + } + + Log.i(LOG_TAG_VPN, "split tunnel proxy($proxyId): ipv4? ${pair.first}, ipv6? ${pair.second}, res? $res") + res + } catch (e: Exception) { + Log.w(LOG_TAG_VPN, "err isSplitTunnelProxy($proxyId): ${e.message}") + false + } + } + + fun getActiveProxiesIpVersion(): BraveVPNService.OverlayNetworks { + try { + val router = tunnel.proxies.router() + val has4 = router.iP4() + val has6 = router.iP6() + val failOpen = !router.iP4() && !router.iP6() + Log.i(LOG_TAG_VPN, "proxy ip version, has4? $has4, has6? $has6, failOpen? $failOpen") + return BraveVPNService.OverlayNetworks(has4, has6, failOpen) + } catch (e: Exception) { + Log.w(LOG_TAG_VPN, "err proxy ip version: ${e.message}") + } + return BraveVPNService.OverlayNetworks() } private fun ui(f: suspend () -> Unit) { diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index 35faab675..f4834c0da 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -35,7 +35,10 @@ import android.net.VpnService import android.os.Build.VERSION import android.os.Build.VERSION_CODES import android.os.ParcelFileDescriptor +import android.os.SystemClock import android.os.SystemClock.elapsedRealtime +import android.system.OsConstants.AF_INET +import android.system.OsConstants.AF_INET6 import android.util.Log import android.view.accessibility.AccessibilityManager import android.widget.Toast @@ -50,6 +53,7 @@ import backend.RDNS import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.data.AppConfig +import com.celzero.bravedns.data.AppConfig.Companion.LOOPBACK_DNS import com.celzero.bravedns.data.ConnTrackerMetaData import com.celzero.bravedns.data.ConnectionSummary import com.celzero.bravedns.database.AppInfo @@ -99,12 +103,14 @@ import java.util.Locale import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.abs import kotlin.math.min import kotlin.random.Random import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope -import kotlinx.coroutines.cancel +import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.withLock @@ -144,6 +150,9 @@ class BraveVPNService : private const val IPV6_PREFIX_LENGTH: Int = 120 const val VPN_INTERFACE_MTU: Int = 1500 + + // TODO: add routes as normal but do not send fd to netstack + const val FAIL_OPEN_ON_NO_NETWORK = true } private var isLockDownPrevious: Boolean = false @@ -183,10 +192,23 @@ class BraveVPNService : private var excludedApps: MutableSet = mutableSetOf() - var underlyingNetworks: ConnectionMonitor.UnderlyingNetworks? = null + // post underlying networks as live data + @Volatile var underlyingNetworks: ConnectionMonitor.UnderlyingNetworks? = null + @Volatile var overlayNetworks: OverlayNetworks = OverlayNetworks() private var accessibilityListener: AccessibilityManager.AccessibilityStateChangeListener? = null + data class OverlayNetworks( + var has4: Boolean = false, + var has6: Boolean = false, + var failOpen: Boolean = true + ) + + data class Networks( + val underlyingNws: ConnectionMonitor.UnderlyingNetworks?, + val overlayNws: OverlayNetworks + ) + enum class State { NEW, WORKING, @@ -204,6 +226,7 @@ class BraveVPNService : override fun bind4(who: String, fid: Long) { val rinr = persistentState.routeRethinkInRethink + val curnet = underlyingNetworks logd("protect: $who, $fid, rinr? $rinr") if (rinr && who != Backend.Exit) { // do not proceed if rethink within rethink is enabled and proxyId(who) is not exit @@ -213,14 +236,14 @@ class BraveVPNService : this.protect(fid.toInt()) // binding to the underlying network is not working. // no need to bind if use active network is true - if (underlyingNetworks?.useActive == true) { + if (curnet?.useActive == true) { logd("bind4: use active network is true") return } var pfd: ParcelFileDescriptor? = null // allNet, ipv4Net, ipv6Net is always sorted, first network is always the active network - underlyingNetworks?.ipv4Net?.forEach { + curnet?.ipv4Net?.forEach { try { pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) @@ -233,11 +256,12 @@ class BraveVPNService : pfd?.detachFd() } } - logd("bind4: no network to bind, ${underlyingNetworks?.ipv4Net?.size}") + logd("bind4: no network to bind, ${curnet?.ipv4Net?.size}") } override fun bind6(who: String, fid: Long) { val rinr = persistentState.routeRethinkInRethink + val curnet = underlyingNetworks logd("protect: $who, $fid, rinr? $rinr") if (rinr && who != Backend.Exit) { // do not proceed if rethink within rethink is enabled and proxyId(who) is not exit @@ -248,13 +272,13 @@ class BraveVPNService : // who is not used, but kept for future use // binding to the underlying network is not working. // no need to bind if use active network is true - if (underlyingNetworks?.useActive == true) { + if (curnet?.useActive == true) { logd("bind6: use active network is true") return } var pfd: ParcelFileDescriptor? = null - underlyingNetworks?.ipv6Net?.forEach { + curnet?.ipv6Net?.forEach { try { pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) it.network.bindSocket(pfd!!.fileDescriptor) @@ -266,20 +290,20 @@ class BraveVPNService : pfd?.detachFd() } } - logd("bind6: no network to bind, ${underlyingNetworks?.ipv6Net?.size}") + logd("bind6: no network to bind, ${curnet?.ipv6Net?.size}") } override fun protect(who: String?, fd: Long) { val rinr = persistentState.routeRethinkInRethink logd("protect: $who, $fd, rinr? $rinr") if (who != Backend.Exit && rinr) { - // do not proceed if rethink within rethink is enabled and proxyId(who) is not exit + // when in rinr mode, only protect "Exit" as others must be looped back into the tunnel return } this.protect(fd.toInt()) } - private fun getUid( + private suspend fun getUid( _uid: Long, protocol: Int, srcIp: String, @@ -288,7 +312,8 @@ class BraveVPNService : dstPort: Int ): Int { return if (VERSION.SDK_INT >= VERSION_CODES.Q) { - connTracer.getUidQ(protocol, srcIp, srcPort, dstIp, dstPort) + ioAsync("getUidQ") { connTracer.getUidQ(protocol, srcIp, srcPort, dstIp, dstPort) } + .await() } else { _uid.toInt() // uid must have been retrieved from procfs by the caller } @@ -626,6 +651,10 @@ class BraveVPNService : return KnownPorts.isDns(port) } + private fun isPrivateDns(port: Int): Boolean { + return KnownPorts.isDoT(port) + } + // Modified the logic of "Block connections when screen is locked". // Earlier the screen lock is detected with receiver received for Action // user_present/screen_off. @@ -663,9 +692,10 @@ class BraveVPNService : } private fun isConnectionMetered(dst: String): Boolean { + val curnet = underlyingNetworks // assume active network until underlying networks are set by ConnectionMonitor // do not use persistentState.useMultipleNetworks - val useActive = underlyingNetworks == null || underlyingNetworks?.useActive == true + val useActive = curnet == null || curnet.useActive if (!useActive || VpnController.isVpnLockdown()) { return isIfaceMetered(dst) } @@ -680,19 +710,20 @@ class BraveVPNService : } // TODO: check for all networks instead of just the first one + val curnet = underlyingNetworks val cap = if (dest.isZero || dest.isIPv6) { // wildcard addrs(::80, ::443, etc.) are bound to ipv6 // if there are no network to be bound, fallback to active network - if (underlyingNetworks?.ipv6Net?.isEmpty() == true) { + if (curnet?.ipv6Net?.isEmpty() == true) { return isActiveIfaceMetered() } - underlyingNetworks?.ipv6Net?.firstOrNull()?.capabilities + curnet?.ipv6Net?.firstOrNull()?.capabilities } else { // if there are no network to be bound, fallback to active network - if (underlyingNetworks?.ipv4Net?.isEmpty() == true) { + if (curnet?.ipv4Net?.isEmpty() == true) { return isActiveIfaceMetered() } - underlyingNetworks?.ipv4Net?.firstOrNull()?.capabilities + curnet?.ipv4Net?.firstOrNull()?.capabilities } // if there are no network to be bound given a destination IP, fallback to active network @@ -704,16 +735,14 @@ class BraveVPNService : } private fun isActiveIfaceMetered(): Boolean { + val curnet = underlyingNetworks ?: return false // assume unmetered val now = elapsedRealtime() - val ts = underlyingNetworks?.lastUpdated - // Added the INIT_TIME_MS check, encountered a bug during phone restart - // isAccessibilityServiceRunning default value(false) is passed instead of - // checking it from accessibility service for the first time. - if (ts == null || Math.abs(now - ts) > Constants.ACTIVE_NETWORK_CHECK_THRESHOLD_MS) { - underlyingNetworks?.lastUpdated = now - underlyingNetworks?.isActiveNetworkMetered = connectivityManager.isActiveNetworkMetered + val ts = curnet.lastUpdated + if (abs(now - ts) > Constants.ACTIVE_NETWORK_CHECK_THRESHOLD_MS) { + curnet.lastUpdated = now + curnet.isActiveNetworkMetered = connectivityManager.isActiveNetworkMetered } - return underlyingNetworks?.isActiveNetworkMetered == true + return curnet.isActiveNetworkMetered } private fun isAppBlocked(connectionStatus: FirewallManager.ConnectionStatus): Boolean { @@ -857,28 +886,43 @@ class BraveVPNService : } } - /** - * Records the network transaction in local database The logs will be shown in network monitor - * screen - */ - private fun connTrack(info: ConnTrackerMetaData?) { - if (info == null) return - - netLogTracker.writeIpLog(info) + private fun canAllowBypass(): Boolean { + return !VpnController.isVpnLockdown() && + persistentState.allowBypass && + !appConfig.isProxyEnabled() } private suspend fun newBuilder(): Builder { var builder = Builder() - if (!VpnController.isVpnLockdown() && persistentState.allowBypass) { + if (canAllowBypass()) { Log.i(LOG_TAG_VPN, "allow apps to bypass vpn on-demand") builder = builder.allowBypass() + // TODO: should allowFamily be set? + // family must be either AF_INET (for IPv4) or AF_INET6 (for IPv6) } - // underlying networks is set to null, which prompts Android to set it to whatever is the - // current active network. Later, ConnectionMonitor#onVpnStarted, depending on user - // chosen preferences, sets appropriate underlying network/s. - builder.setUnderlyingNetworks(null) + val curnet = underlyingNetworks + if (curnet == null || curnet.useActive) { + // underlying networks is set to null, which prompts Android to set it to whatever is + // the current active network. Later, ConnectionMonitor#onVpnStarted, depending on user + // chosen preferences, sets appropriate underlying network/s. + builder.setUnderlyingNetworks(null) + } else { + // add ipv4 and ipv6 networks to the tunnel + val ipv4 = curnet.ipv4Net.map { it.network } + val ipv6 = curnet.ipv6Net.map { it.network } + val allNetworks = ipv4.plus(ipv6) + if (allNetworks.isNotEmpty()) { + builder.setUnderlyingNetworks(allNetworks.toTypedArray()) + } else { + if (FAIL_OPEN_ON_NO_NETWORK) { + builder.setUnderlyingNetworks(null) + } else { + builder.setUnderlyingNetworks(emptyArray()) + } + } + } // Fix - Cloud Backups were failing thinking that the VPN connection is metered. // The below code will fix that. @@ -886,53 +930,62 @@ class BraveVPNService : builder.setMetered(false) } - // re-hydrate exclude-apps incase it has changed in the interim - excludedApps = FirewallManager.getExcludedApps() + // route rethink traffic in rethink based on the user selection + if (!persistentState.routeRethinkInRethink) { + addDisallowedApplication(builder, this.packageName) + } else { + Log.i(LOG_TAG_VPN, "builder: route rethink traffic in rethink") + // no-op + } - try { - if (isAppPaused()) { // if app is paused, exclude all non-firewalled apps and be done - if (VpnController.isVpnLockdown()) { - Log.i(LOG_TAG_VPN, "paused but vpn is lockdown; cannot exclude apps") - return builder - } - val nonFirewalledApps = FirewallManager.getNonFirewalledAppsPackageNames() - Log.i( - LOG_TAG_VPN, - "paused, exclude non-firewalled apps, size: ${nonFirewalledApps.count()}" - ) - nonFirewalledApps.forEach { builder.addDisallowedApplication(it.packageName) } + if (isAppPaused()) { // exclude all non-firewalled apps and be done + if (VpnController.isVpnLockdown()) { + Log.i(LOG_TAG_VPN, "paused but vpn is lockdown; cannot exclude apps") return builder } + val nonFirewalledApps = FirewallManager.getNonFirewalledAppsPackageNames() + val packages = nonFirewalledApps.map { it.packageName } + Log.i(LOG_TAG_VPN, "paused, exclude non-firewalled apps, size: ${packages.count()}") + addDisallowedApplication(builder, packages) + return builder + } - if (appConfig.determineFirewallMode().isFirewallSinkMode()) { - for (packageName in excludedApps) { - builder = builder.addAllowedApplication(packageName) - } + // re-hydrate exclude-apps incase it has changed in the interim + excludedApps = FirewallManager.getExcludedApps() + if (appConfig.determineFirewallMode().isFirewallSinkMode()) { + addAllowedApplication(builder, excludedApps) + } else { + // ignore excluded-apps settings when vpn is lockdown because + // those apps would lose all internet connectivity, otherwise + if (!VpnController.isVpnLockdown()) { + addDisallowedApplication(builder, excludedApps) } else { - // ignore excluded-apps settings when vpn is lockdown because - // those apps would lose all internet connectivity, otherwise - if (!VpnController.isVpnLockdown()) { - excludedApps.forEach { - builder = builder.addDisallowedApplication(it) - Log.i(LOG_TAG_VPN, "builder, exclude package: $it") - } - } else { - Log.w(LOG_TAG_VPN, "builder, vpn is lockdown, ignoring exclude-apps list") - } + Log.w(LOG_TAG_VPN, "builder, vpn is lockdown, ignoring exclude-apps list") } + } + return builder + } - // route rethink traffic in rethink based on the user selection - if (!persistentState.routeRethinkInRethink) { - Log.i(LOG_TAG_VPN, "builder, do not route rethink traffic in rethink") - builder = builder.addDisallowedApplication(this.packageName) - } else { - Log.i(LOG_TAG_VPN, "builder, route rethink traffic in rethink") - // no-op - } + private fun addDisallowedApplication(builder: Builder, pkg: String) { + try { + builder.addDisallowedApplication(pkg) } catch (e: PackageManager.NameNotFoundException) { - Log.w(LOG_TAG_VPN, "cannot exclude the dns proxy app", e) + Log.w(LOG_TAG_VPN, "skip adding disallowed app ($pkg)", e) + } + } + + private fun addDisallowedApplication(builder: Builder, packages: Collection) { + packages.forEach { addDisallowedApplication(builder, it) } + } + + private fun addAllowedApplication(builder: Builder, packages: Set) { + packages.forEach { + try { + builder.addAllowedApplication(it) + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG_VPN, "skip adding allowed app ($it)", e) + } } - return builder } override fun onCreate() { @@ -985,7 +1038,7 @@ class BraveVPNService : Log.i(LOG_TAG_VPN, "excluded-apps list changed, restart vpn") - io("excludeApps") { restartVpnWithExistingAppConfig() } + io("excludeApps") { restartVpnWithNewAppConfig() } } catch (e: Exception) { // NoSuchElementException, ConcurrentModification Log.e(LOG_TAG_VPN, "error retrieving value from appInfos observer ${e.message}", e) } @@ -1016,6 +1069,7 @@ class BraveVPNService : } else { builder = NotificationCompat.Builder(this, MAIN_CHANNEL_ID) } + val isProxyEnabled = appConfig.isProxyEnabled() var contentTitle: String = when (appConfig.getBraveMode()) { @@ -1023,7 +1077,11 @@ class BraveVPNService : AppConfig.BraveMode.FIREWALL -> resources.getString(R.string.firewall_mode_notification_title) AppConfig.BraveMode.DNS_FIREWALL -> - resources.getString(R.string.hybrid_mode_notification_title) + if (isProxyEnabled) { + resources.getString(R.string.hybrid_mode_with_proxy_notification_title) + } else { + resources.getString(R.string.hybrid_mode_notification_title) + } } if (isAppPaused()) { @@ -1164,16 +1222,6 @@ class BraveVPNService : // startForeground should always be called within 5 secs of onStartCommand invocation // https://developer.android.com/guide/components/fg-service-types - // java.lang.IllegalArgumentException: foregroundServiceType 0x00000400 is - // not a subset of foregroundServiceType attribute 0x00000000 in service element of - // manifest file (below code is commented out until complete analysis of - // FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED) (v055b) - /*ServiceCompat.startForeground( - this, - SERVICE_ID, - updateNotificationBuilder().build(), - FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED - )*/ if (isAtleastU()) { startForeground( SERVICE_ID, @@ -1223,7 +1271,10 @@ class BraveVPNService : // refresh should happen before restartVpn, otherwise the new vpn will not // have app, ip, domain rules. See RefreshDatabase#refresh rdb.refresh(RefreshDatabase.ACTION_REFRESH_AUTO) { - restartVpn(opts) + restartVpn(opts, Networks(null, overlayNetworks)) + controllerProto = Pair(overlayNetworks.has4, overlayNetworks.has6) + // update the controller, which will update the UI (home screen btm sheet) + VpnController.updateProtocol(controllerProto) // call this *after* a new vpn is created #512 uiCtx("observers") { observeChanges() } } @@ -1317,7 +1368,7 @@ class BraveVPNService : logd("on pref change, key: $key") when (key) { PersistentState.BRAVE_MODE -> { - io("modeChange") { setTunMode() } + io("braveModeChange") { setTunMode() } notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) } PersistentState.LOCAL_BLOCK_LIST -> { @@ -1377,17 +1428,22 @@ class BraveVPNService : } } PersistentState.PREVENT_DNS_LEAKS -> { - io("preventDnsLeaks") { restartVpn(createNewTunnelOptsObj()) } + io("preventDnsLeaks") { setTunMode() } } PersistentState.DNS_RELAYS -> { // FIXME: add relay using vpnAdapter; no update needed io("updateDnscrypt") { addTransport() } } PersistentState.ALLOW_BYPASS -> { - io("allowBypass") { restartVpn(createNewTunnelOptsObj()) } + io("allowBypass") { restartVpnWithNewAppConfig() } } PersistentState.PROXY_TYPE -> { - io("proxy") { handleProxyChange() } + io("proxy") { + handleProxyChange() + // if any proxy is set, then disable builder.allowByPass as false + disableAllowBypassIfNeeded() + } + notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) } PersistentState.NETWORK -> { io("useAllNetworks") { notifyConnectionMonitor() } @@ -1417,15 +1473,31 @@ class BraveVPNService : } PersistentState.PRIVATE_IPS -> { // restart vpn to enable/disable route lan traffic - io("routeLanTraffic") { restartVpn(createNewTunnelOptsObj()) } + io("routeLanTraffic") { restartVpnWithNewAppConfig() } } PersistentState.RETHINK_IN_RETHINK -> { // restart vpn to allow/disallow rethink traffic in rethink - io("routeRethinkInRethink") { restartVpn(createNewTunnelOptsObj()) } + io("routeRethinkInRethink") { restartVpnWithNewAppConfig() } } } } + private suspend fun disableAllowBypassIfNeeded() { + if (appConfig.isProxyEnabled() && persistentState.allowBypass) { + Log.i(LOG_TAG_VPN, "disabling allowBypass, as proxy is set.") + // inform user about the change in allow bypass setting by showing a toast + ui { + val message = + getString( + R.string.toast_allow_bypass_disabled, + getString(R.string.settings_allow_bypass_heading) + ) + showToastUiCentered(this, message, Toast.LENGTH_LONG) + } + persistentState.allowBypass = false + } + } + private suspend fun setRDNS() { logd("set brave dns mode, local/remote") vpnAdapter?.setRDNS() @@ -1459,7 +1531,7 @@ class BraveVPNService : // route changes as informed by the connection monitor notifyConnectionMonitor() } - restartVpn(createNewTunnelOptsObj()) + restartVpnWithNewAppConfig() setRoute() } @@ -1475,8 +1547,7 @@ class BraveVPNService : vpnAdapter?.setTcpProxy() } AppConfig.ProxyProvider.WIREGUARD -> { - // update wireguard config - vpnAdapter?.setWireguardTunnelModeIfNeeded(tunProxyMode) + // no need to set proxy for wireguard, as WireguardManager handles it } AppConfig.ProxyProvider.ORBOT -> { // update orbot config, its treated as SOCKS5 or HTTP proxy internally @@ -1489,19 +1560,6 @@ class BraveVPNService : } } - private fun createNewTunnelOptsObj(): AppConfig.TunnelOptions { - val opts = - appConfig.newTunnelOptions( - this, - getFakeDns(), - appConfig.getInternetProtocol(), - appConfig.getProtocolTranslationMode(), - mtu() - ) - Log.i(LOG_TAG_VPN, "created new tunnel options, opts: $opts") - return opts - } - private fun spawnLocalBlocklistStampUpdate() { io("dnsStampUpdate") { vpnAdapter?.setRDNSStamp() } } @@ -1533,7 +1591,12 @@ class BraveVPNService : } } - private suspend fun restartVpnWithExistingAppConfig() { + private suspend fun restartVpnWithNewAppConfig( + underlyingNws: ConnectionMonitor.UnderlyingNetworks? = underlyingNetworks, + overlayNws: OverlayNetworks = overlayNetworks + ) { + logd("restart vpn with new app config") + val nws = Networks(underlyingNws, overlayNws) restartVpn( appConfig.newTunnelOptions( this, @@ -1541,8 +1604,11 @@ class BraveVPNService : appConfig.getInternetProtocol(), appConfig.getProtocolTranslationMode(), mtu() - ) + ), + nws ) + // update the controller, which will update the UI (home screen btm sheet) + VpnController.updateProtocol(controllerProto) } private suspend fun setPcapMode() { @@ -1571,7 +1637,7 @@ class BraveVPNService : vpnAdapter?.setTunMode(tunnelOptions) } - private suspend fun restartVpn(opts: AppConfig.TunnelOptions) { + private suspend fun restartVpn(opts: AppConfig.TunnelOptions, networks: Networks) { var tunFd: ParcelFileDescriptor? = null VpnController.mutex.withLock { // connectionMonitor = null indicates onStartCommand has not yet been called @@ -1597,7 +1663,7 @@ class BraveVPNService : } // attempt seamless hand-off as described in VpnService.Builder.establish() docs - tunFd = establishVpn() + tunFd = establishVpn(networks) if (tunFd == null) { Log.e(LOG_TAG_VPN, "cannot restart-vpn, no tun-fd") io("noTunRestart") { signalStopService(userInitiated = false) } @@ -1672,13 +1738,17 @@ class BraveVPNService : // TODO: #294 - Figure out a way to show users that the device is offline instead of status as // failing. - override fun onNetworkDisconnected(newNetworks: ConnectionMonitor.UnderlyingNetworks) { - underlyingNetworks = newNetworks - Log.i( - LOG_TAG_VPN, - "onNetworkDisconnected: Underlying networks: null, state: z, $underlyingNetworks" - ) - setUnderlyingNetworks(null) + override fun onNetworkDisconnected(networks: ConnectionMonitor.UnderlyingNetworks) { + underlyingNetworks = networks + Log.i(LOG_TAG_VPN, "onNetworkDisconnected: state: z, $networks") + if (FAIL_OPEN_ON_NO_NETWORK) { + setUnderlyingNetworks(null) + } else { + setUnderlyingNetworks(emptyArray()) + // setting empty array is not enough, do not add routes to the tunnel + io("nwDisconnect") { restartVpnWithNewAppConfig(networks) } + } + setNetworkAndDefaultDnsIfNeeded() VpnController.onConnectionStateChanged(null) } @@ -1688,13 +1758,14 @@ class BraveVPNService : } override fun onNetworkConnected(newNetworks: ConnectionMonitor.UnderlyingNetworks) { - val isRoutesChanged = isUnderlyingRoutesChanged(underlyingNetworks, newNetworks) - val isMtuChanged = isMtuChanged(underlyingNetworks, newNetworks) + val curnet = underlyingNetworks + val out = interestingNetworkChanges(curnet, newNetworks) + val isRoutesChanged = out.routesChanged + val activeRoutesChanged = out.activeRoutesChanged + val isBoundNetworksChanged = out.netChanged + val isMtuChanged = out.mtuChanged underlyingNetworks = newNetworks - Log.i( - LOG_TAG_VPN, - "onNetworkConnected: change in routes: $isRoutesChanged, mtu: $isMtuChanged, $underlyingNetworks" - ) + Log.i(LOG_TAG_VPN, "onNetworkConnected: changes: $out for new: $newNetworks") // always reset the system dns server ip of the active network with the tunnel setNetworkAndDefaultDnsIfNeeded() @@ -1703,72 +1774,109 @@ class BraveVPNService : setUnderlyingNetworks(null) } else if (newNetworks.ipv4Net.isEmpty() && newNetworks.ipv6Net.isEmpty()) { Log.w(LOG_TAG_VPN, "network changed but empty ipv4/ipv6 networks w connectivity") - setUnderlyingNetworks(null) + if (FAIL_OPEN_ON_NO_NETWORK) { + setUnderlyingNetworks(null) + } else { + setUnderlyingNetworks(emptyArray()) + } } else { // add ipv4/ipv6 networks to the tunnel val allNetworks = newNetworks.ipv4Net.map { it.network } + newNetworks.ipv6Net.map { it.network } setUnderlyingNetworks(allNetworks.toTypedArray()) } - // restart vpn if the routes(in auto mode) or mtu changes - if (isMtuChanged || (appConfig.getInternetProtocol().isIPv46() && isRoutesChanged)) { - io("RouteOrMtuChanges") { restartVpnWithExistingAppConfig() } + // restart vpn if the routes (+ auto mode) or when mtu changes + if (isMtuChanged || isRouteChanged(out, underlyingNetworks?.useActive == true)) { + logd("mtu $isMtuChanged or routes/active routes $isRoutesChanged/$activeRoutesChanged changed, restart vpn") + io("nwConnect") { restartVpnWithNewAppConfig(newNetworks) } } - // Workaround for WireGuard connection issues after network change // WireGuard may fail to connect to the server when the network changes. // refresh will do a configuration refresh in tunnel to ensure a successful // reconnection after detecting a network change event - if (appConfig.isWireGuardEnabled()) { + if (isBoundNetworksChanged && appConfig.isWireGuardEnabled()) { refreshProxies() } } + private fun isRouteChanged(out: NetworkChanges, useActive: Boolean): Boolean { + // no need to check for routes if the app is not set to auto mode + if (!appConfig.getInternetProtocol().isIPv46()) { + return false + } + + // if use active is set, then only consider active routes + if (useActive) { + return out.activeRoutesChanged + } + + // if use active is not set, then consider all the routes + return out.routesChanged + } + private fun setRoute() { logd("set route") // go / netstack is always routing dual-stack, regardless // io("setRoute") { vpnAdapter?.setRoute(createNewTunnelOptsObj()) } } - private fun isUnderlyingRoutesChanged( + data class NetworkChanges( + val routesChanged: Boolean = true, + val activeRoutesChanged: Boolean = true, + val netChanged: Boolean = true, + val mtuChanged: Boolean = true + ) + + private fun interestingNetworkChanges( old: ConnectionMonitor.UnderlyingNetworks?, new: ConnectionMonitor.UnderlyingNetworks - ): Boolean { + ): NetworkChanges { // no old routes to compare with, return true - if (old == null) return true + if (old == null) return NetworkChanges() - if (new.useActive) { - // if useActive is true, then check first network is changed - // chances are that the active network had only v4/v6 previously - val oldActive4 = old.ipv4Net.firstOrNull() - val newActive4 = new.ipv4Net.firstOrNull() - val oldActive6 = old.ipv6Net.firstOrNull() - val newActive6 = new.ipv6Net.firstOrNull() - return oldActive4 != newActive4 || oldActive6 != newActive6 - } + val mtuChanged = old.minMtu != new.minMtu - // if useActive is false, then check if ipv6 or ipv4 routes changed + if (new.useActive) { + connectivityManager.activeNetwork?.let { new2 -> + // check if current active network, new2, also exists in old networks + // if so, routes have remained the same despite network changes + val had4 = old.ipv4Net.any { isNetworkSame(it.network, new2) } + val had6 = old.ipv6Net.any { isNetworkSame(it.network, new2) } + val routesChanged = !had4 || !had6 + // in case of use active, only consider first network as active + val hadActive4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, new2) + val hadActive6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, new2) + val activeRoutesChanged = !hadActive4 || !hadActive6 + // fixme: similar to actievRoutesChanged, should remove this? + // check if the first networks are different + val oldnet6 = old.ipv6Net.firstOrNull()?.network + val oldnet4 = old.ipv4Net.firstOrNull()?.network + val netChanged = !isNetworkSame(oldnet6, new2) || !isNetworkSame(oldnet4, new2) + + return NetworkChanges(routesChanged, activeRoutesChanged, netChanged, mtuChanged) + } + // active network unknown, fallthrough + } + + // check if ipv6 or ipv4 routes changed val old6 = old.ipv6Net.isNotEmpty() val new6 = new.ipv6Net.isNotEmpty() val old4 = old.ipv4Net.isNotEmpty() val new4 = new.ipv4Net.isNotEmpty() - // inform if ipv6 or ipv4 routes changed - return old6 != new6 || old4 != new4 - } + val routesChanged = old6 != new6 || old4 != new4 + // check if the first networks are different + val oldnet6 = old.ipv6Net.firstOrNull()?.network + val newnet6 = new.ipv6Net.firstOrNull()?.network + val oldnet4 = old.ipv4Net.firstOrNull()?.network + val newnet4 = new.ipv4Net.firstOrNull()?.network + val netChanged = !isNetworkSame(oldnet6, newnet6) || !isNetworkSame(oldnet4, newnet4) - private fun isMtuChanged( - old: ConnectionMonitor.UnderlyingNetworks?, - new: ConnectionMonitor.UnderlyingNetworks - ): Boolean { - return if (old == null) { - return true - } else { - new.minMtu != old.minMtu - } + return NetworkChanges(routesChanged, netChanged, mtuChanged) } private fun setNetworkAndDefaultDnsIfNeeded() { - val useActive = underlyingNetworks == null || underlyingNetworks?.useActive == true + val currNet = underlyingNetworks + val useActive = currNet == null || currNet.useActive val dnsServers = if ( persistentState.routeRethinkInRethink || // rinr case is same as multiple networks @@ -1776,13 +1884,13 @@ class BraveVPNService : VpnController.isVpnLockdown() // active nw is null when lockdown ) { val dl: MutableList = mutableListOf() - underlyingNetworks?.ipv4Net?.forEach { + currNet?.ipv4Net?.forEach { val n = it.network val lp = it.linkProperties Log.i(LOG_TAG_VPN, "dns servers for network: $n, $lp") lp?.dnsServers?.let { it1 -> dl.addAll(it1) } } - underlyingNetworks?.ipv6Net?.forEach { + currNet?.ipv6Net?.forEach { val n = it.network val lp = it.linkProperties Log.i(LOG_TAG_VPN, "dns servers for network: $n, $lp") @@ -1792,12 +1900,42 @@ class BraveVPNService : } else { // get dns servers from the first network or active network val active = connectivityManager.activeNetwork - // first network is considered to be active network - val fnw = - underlyingNetworks?.ipv4Net?.firstOrNull() - ?: underlyingNetworks?.ipv6Net?.firstOrNull() - fnw?.linkProperties?.dnsServers - ?: connectivityManager.getLinkProperties(active)?.dnsServers + val lp = connectivityManager.getLinkProperties(active) + val dnsServers = lp?.dnsServers + + if (dnsServers.isNullOrEmpty()) { + // first network is considered to be active network + val ipv4 = currNet?.ipv4Net?.firstOrNull() + val ipv6 = currNet?.ipv6Net?.firstOrNull() + val isv4Active = isNetworkSame(ipv4?.network, active) + val isv6Active = isNetworkSame(ipv6?.network, active) + val dns4 = ipv4?.linkProperties?.dnsServers + val dns6 = ipv6?.linkProperties?.dnsServers + if (isv4Active && isv6Active) { + val dl = mutableListOf() + dns4?.let { dl.addAll(it) } + dns6?.let { dl.addAll(it) } + Log.i(LOG_TAG_VPN, "dns servers for network: $dl") + dl + } else if (isv4Active) { + Log.i(LOG_TAG_VPN, "dns servers for network: $dns4") + dns4 + } else if (isv6Active) { + Log.i(LOG_TAG_VPN, "dns servers for network: $dns6") + dns6 + } else { + // if active network is not found in the list of networks, then use dns from + // first network + val dl = mutableListOf() + dns4?.let { dl.addAll(it) } + dns6?.let { dl.addAll(it) } + Log.i(LOG_TAG_VPN, "dns servers for network: $dl") + dl + } + } else { + Log.i(LOG_TAG_VPN, "dns servers for network: $dnsServers") + dnsServers + } } if (dnsServers.isNullOrEmpty()) { @@ -1823,41 +1961,23 @@ class BraveVPNService : vpnAdapter?.setSystemDns(dns) // set default dns server for the tunnel if none is set if (isDefaultDnsNone()) { - val firstDns = dns.firstOrNull() - val d = HostName(IPAddressString(firstDns).address, KnownPorts.DNS_PORT).toString() - vpnAdapter?.addDefaultTransport(d) + val dnsCsv = dns.joinToString(",") + vpnAdapter?.addDefaultTransport(dnsCsv) } } } private fun determineSystemDns(dnsServers: List?): List { - val fallbackIp = "8.8.4.4" val dnsList: MutableList = mutableListOf() - if (dnsServers.isNullOrEmpty()) { - Log.w(LOG_TAG_VPN, "No System DNS servers; unsetting existing $fallbackIp") - dnsList.add(fallbackIp) - return dnsList - } - try { - when (appConfig.getInternetProtocol()) { - InternetProtocol.IPv4 -> { - val list = dnsServers.filter { IPAddressString(it.hostAddress).isIPv4 } - val sys = list.map { it.hostAddress ?: "" }.filter { it != "" } - dnsList.addAll(sys) - } - InternetProtocol.IPv6 -> { - val list = dnsServers.filter { IPAddressString(it.hostAddress).isIPv6 } - val sys = list.map { it.hostAddress ?: "" }.filter { it != "" } - dnsList.addAll(sys) - } - InternetProtocol.IPv46 -> { - val list = dnsServers.map { it.hostAddress ?: "" }.filter { it != "" } - dnsList.addAll(list) - } - } - } catch (e: NoSuchElementException) { - Log.w(LOG_TAG_VPN, "No ip4 / ip6 matching with dns servers") + val list = dnsServers?.map { it.hostAddress ?: "" }?.filter { it != "" } + + if (list.isNullOrEmpty()) { + // regardless of rinr loopback mode, always use LOOPBACK_DNS to avoid leaks + Log.w(LOG_TAG_VPN, "No System DNS servers; unsetting existing $LOOPBACK_DNS") + dnsList.add(LOOPBACK_DNS) + } else { + dnsList.addAll(list) } if (DEBUG) Log.d(LOG_TAG_VPN, "System dns: $dnsList") return dnsList @@ -1871,10 +1991,8 @@ class BraveVPNService : private fun handleVpnLockdownStateAsync() { if (!syncLockdownState()) return - io("lockdownSync") { - Log.i(LOG_TAG_VPN, "vpn lockdown mode change, restarting") - restartVpnWithExistingAppConfig() - } + Log.i(LOG_TAG_VPN, "vpn lockdown mode change, restarting") + io("lockdownSync") { restartVpnWithNewAppConfig() } } private fun syncLockdownState(): Boolean { @@ -1946,7 +2064,6 @@ class BraveVPNService : } } VpnController.onVpnDestroyed() - vpnScope.cancel("VpnService onDestroy") Log.w(LOG_TAG_VPN, "Destroying VPN service") @@ -1990,7 +2107,7 @@ class BraveVPNService : } private fun handleVpnServiceOnAppStateChange() { // paused or resumed - io("appStateChange") { restartVpnWithExistingAppConfig() } + io("pauseOrResumed") { restartVpnWithNewAppConfig() } ui { notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) } } @@ -2017,13 +2134,16 @@ class BraveVPNService : } } - private suspend fun establishVpn(): ParcelFileDescriptor? { + private var controllerProto: Pair = Pair(false, false) + + private suspend fun establishVpn(networks: Networks): ParcelFileDescriptor? { try { val mtu = mtu() // get mtu from the underlyingnetworks var builder: VpnService.Builder = newBuilder().setSession("Rethink").setMtu(mtu) - var has6 = route6() - var has4 = route4() + var has6 = route6(networks) + var has4 = route4(networks) + controllerProto = Pair(has4, has6) Log.i(LOG_TAG_VPN, "Building vpn for v4? $has4, v6? $has6") @@ -2031,9 +2151,9 @@ class BraveVPNService : // no route available for both v4 and v6, add all routes // connectivity manager is expected to retry when no route is available // see ConnectionMonitor#repopulateTrackedNetworks - Log.i(LOG_TAG_VPN, "No route available for v4 and v6, adding all routes") - has4 = true - has6 = true + Log.i(LOG_TAG_VPN, "No route available, fail-opn? $FAIL_OPEN_ON_NO_NETWORK") + has4 = FAIL_OPEN_ON_NO_NETWORK + has6 = FAIL_OPEN_ON_NO_NETWORK } // setup the gateway addr @@ -2063,6 +2183,16 @@ class BraveVPNService : if (has6) { builder = addRoute6(builder) } + } else { + // when not routing all traffic (firewall inactive) allow v4/v6 families + // to be routed based on the underlying network (bypassing the tunnel) + Log.i(LOG_TAG_VPN, "dns-only mode, allowFamily: v4: $has4, v6: $has6") + if (has4) { + builder.allowFamily(AF_INET) + } + if (has6) { + builder.allowFamily(AF_INET6) + } } return builder.establish() } catch (e: Exception) { @@ -2071,7 +2201,7 @@ class BraveVPNService : } } - private fun route6(): Boolean { + private fun route6(nws: Networks): Boolean { return when (appConfig.getInternetProtocol()) { InternetProtocol.IPv4 -> { false @@ -2080,46 +2210,69 @@ class BraveVPNService : true } InternetProtocol.IPv46 -> { + // null overlayNetwork means no active wireguard network, default to true so + // that the route is added based on the underlying network + val overlayIpv6 = nws.overlayNws.has6 || nws.overlayNws.failOpen + + val underlay = nws.underlyingNws // when no underlying-networks are unknown, or if use-multiple-networks is enabled, // simply check whether there are ANY v6 networks available; otherwise, if the vpn // must only use the active-network (always the first network in allNet), then check // if active-network has v6 connectivity (that is, it must be present in ipv6Net). // check if isReachable is true, if not, don't need to add route for v6 (return // false) - logd( - "r6: underlyingNetworks: ${underlyingNetworks?.useActive}, ${underlyingNetworks?.ipv6Net?.size}" - ) - if (underlyingNetworks?.useActive != true) { - return if (underlyingNetworks?.ipv6Net?.size == 0) { + logd("r6: underlyingNetworks: ${underlay?.useActive}, ${underlay?.ipv6Net?.size}") + if (underlay?.useActive != true) { + val underlayIpv6 = (underlay?.ipv6Net?.size ?: 0) > 0 + return if (!underlayIpv6) { Log.i(LOG_TAG_VPN, "r6: No IPv6 networks available") false } else { - Log.i(LOG_TAG_VPN, "r6: IPv6 networks available") - true + Log.i(LOG_TAG_VPN, "r6: IPv6 available, overlay: $overlayIpv6") + // underlay network is available, check if overlay network is available + overlayIpv6 } } else { val activeNetwork = connectivityManager.activeNetwork if (activeNetwork == null) { Log.i(LOG_TAG_VPN, "r6: missing active network, use the first network") - return underlyingNetworks?.ipv6Net?.firstOrNull() != null + return underlay.ipv6Net.isNotEmpty() && overlayIpv6 } - underlyingNetworks?.ipv6Net?.forEach { - if (isNetworkSame(it.network, activeNetwork)) { - Log.i( - LOG_TAG_VPN, - "r6: Active network ok: ${it.network.networkHandle}, ${activeNetwork.networkHandle}" - ) - return true + underlay.ipv6Net.forEach { + val underlayIpv6 = isNetworkSame(it.network, activeNetwork) + if (underlayIpv6) { + Log.i(LOG_TAG_VPN, "r6: Active network ok: ov: $overlayIpv6") + // underlay network is available, check if overlay network is available + return overlayIpv6 } } + Log.i(LOG_TAG_VPN, "r6: active network not available") return false } } } } - private fun route4(): Boolean { + private fun onOverlayNetworkChanged(nw: OverlayNetworks) { + // compare the overlay network pair with the overlayNetworkIpStates to determine if the + // overlay network is changed, if so, restart the vpn + val isRoutesChanged = overlayNetworks != nw + if (isRoutesChanged) { + overlayNetworks = nw + Log.i(LOG_TAG_VPN, "overlay changed $overlayNetworks, restart vpn") + // There may be cases where both overlay and underlay networks have the same routes. + // In such scenarios, no restart is required. However, here the routeChange is + // considered + // only for overlay network changes. Therefore, the VPN needs to be restarted + // to recalculate the decision of adding routes. + io("overlayNwChanged") { restartVpnWithNewAppConfig(overlayNws = overlayNetworks) } + } else { + Log.i(LOG_TAG_VPN, "overlay network not changed, no restart needed") + } + } + + private fun route4(nws: Networks): Boolean { return when (appConfig.getInternetProtocol()) { InternetProtocol.IPv4 -> { true @@ -2128,35 +2281,41 @@ class BraveVPNService : false } InternetProtocol.IPv46 -> { + // null overlayNetwork means no active wireguard network, default to true so + // that the route is added based on the underlying network + val overlayIpv4 = nws.overlayNws.has4 || nws.overlayNws.failOpen + + val underlay = nws.underlyingNws // when no underlying-networks are unknown, or if use-multiple-networks is enabled, // simply check whether there are ANY v4 networks available; otherwise, if the vpn // must only use the active-network (always the first network in allNet), then check // if active-network has v4 connectivity (that is, it must be present in ipv4Net). // check if isReachable is true, if not, don't need to add route for v4 (return // false) - logd( - "r4: underlyingNetworks: ${underlyingNetworks?.useActive}, ${underlyingNetworks?.ipv4Net?.size}" - ) - if (underlyingNetworks?.useActive != true) { - if (underlyingNetworks?.ipv4Net?.size == 0) { + logd("r4: useActive? ${underlay?.useActive}, sz: ${underlay?.ipv4Net?.size}") + if (underlay?.useActive != true) { + val underlayIpv4 = (underlay?.ipv4Net?.size ?: 0) > 0 + if (!underlayIpv4) { Log.i(LOG_TAG_VPN, "r4: No IPv4 networks available") return false } else { Log.i(LOG_TAG_VPN, "r4: IPv4 networks available") - return true + // underlay network is available, check if overlay network is available + return overlayIpv4 } } else { val activeNetwork = connectivityManager.activeNetwork if (activeNetwork == null) { Log.i(LOG_TAG_VPN, "r4: missing active network, use the first network") - return underlyingNetworks?.ipv4Net?.firstOrNull() != null + return underlay.ipv4Net.isNotEmpty() && overlayIpv4 } - underlyingNetworks?.ipv4Net?.forEach { - Log.i(LOG_TAG_VPN, "r4: IPv4 network: ${it.network.networkHandle}") - if (isNetworkSame(it.network, activeNetwork)) { - Log.i(LOG_TAG_VPN, "r4: IPv4 network is reachable") - return true + underlay.ipv4Net.forEach { + val underlayIpv4 = isNetworkSame(it.network, activeNetwork) + if (underlayIpv4) { + Log.i(LOG_TAG_VPN, "r4: reachable, ov: $overlayIpv4") + // underlay network is available, check if overlay network is available + return overlayIpv4 } } return false @@ -2239,9 +2398,9 @@ class BraveVPNService : } catch (ex: UnknownHostException) { Log.e(LOG_TAG_VPN, "addRoute4: ${ex.message}", ex) } - b.addRoute("10.111.222.1", 32) // add route for the gateway - b.addRoute("10.111.222.2", 32) // add route for the router - b.addRoute("10.111.222.3", 32) // add route for the dns + b.addRoute(LanIp.GATEWAY.make(IPV4_TEMPLATE), 32) + b.addRoute(LanIp.DNS.make(IPV4_TEMPLATE), 32) + b.addRoute(LanIp.ROUTER.make(IPV4_TEMPLATE), 32) } else { Log.i(LOG_TAG_VPN, "addRoute4: privateIps is false, adding default route") // no need to exclude LAN traffic, add default route which is 0.0.0.0/0 @@ -2306,12 +2465,17 @@ class BraveVPNService : withContext(CoroutineName(s) + Dispatchers.Main) { f() } } + private suspend fun ioAsync(s: String, f: suspend () -> T): Deferred { + return vpnScope.async(CoroutineName(s) + Dispatchers.IO) { f() } + } + override fun onQuery(fqdn: String?, qtype: Long): backend.DNSOpts = runBlocking { // queryType: see ResourceRecordTypes.kt logd("onQuery: rcvd query: $fqdn, qtype: $qtype") if (fqdn == null) { Log.e(LOG_TAG_VPN, "onQuery: fqdn is null") - return@runBlocking prepareDnsxNsOpts(Backend.Preferred) + // return block all, as it is not expected to reach here + return@runBlocking makeNsOpts(Backend.BlockAll) } if (appConfig.getBraveMode().isDnsMode()) { @@ -2326,7 +2490,7 @@ class BraveVPNService : return@runBlocking res } - val res = prepareDnsxNsOpts(Backend.Preferred) + val res = makeNsOpts(Backend.Preferred) // should not reach here Log.e( LOG_TAG_VPN, "onQuery: unknown mode ${appConfig.getBraveMode()}, $fqdn, returning $res" @@ -2336,152 +2500,119 @@ class BraveVPNService : // function to decide which transport id to return on Dns only mode private fun getTransportIdForDnsMode(fqdn: String): backend.DNSOpts { - // in vpn-lockdown mode+appPause , use system dns if the app is paused to mimic - // as if the apps are excluded from vpn - val useSystemDns = - appConfig.isSystemDns() || (isAppPaused() && VpnController.isVpnLockdown()) - // system dns is treated as special in GoTun2Socks, so return as Dnsx.System if enabled - if (useSystemDns) { - return prepareDnsxNsOpts(Backend.System) - } + val tid = determineDnsTransportId() + // check for global domain rules when (DomainRulesManager.getDomainRule(fqdn, UID_EVERYBODY)) { - DomainRulesManager.Status.TRUST -> - return prepareDnsxNsOpts(Backend.BlockFree) // Dnsx.BlockFree - DomainRulesManager.Status.BLOCK -> - return prepareDnsxNsOpts(Backend.BlockAll) // Dnsx.BlockAll + DomainRulesManager.Status.TRUST -> return makeNsOpts(Backend.BlockFree, true) + DomainRulesManager.Status.BLOCK -> return makeNsOpts(Backend.BlockAll, false) else -> {} // no-op, fall-through; } - return prepareDnsxNsOpts(Backend.Preferred) + return makeNsOpts(tid) } // function to decide which transport id to return on DnsFirewall mode private suspend fun getTransportIdForDnsFirewallMode(fqdn: String): backend.DNSOpts { - return if (FirewallManager.isAnyAppBypassesDns()) { - // if any app is bypassed (dns + firewall) and if local blocklist enabled or remote dns - // is rethink then return Alg so that the decision is made by in flow() function - prepareDnsxNsOpts(Backend.Alg) + val tid = determineDnsTransportId() + val forceBypassLocalBlocklists = isAppPaused() && VpnController.isVpnLockdown() + + return if (forceBypassLocalBlocklists) { + // if the app is paused and vpn is in lockdown mode, then bypass the local blocklists + makeNsOpts(tid, true) + } else if (FirewallManager.isAnyAppBypassesDns()) { + // if any app is bypassed (dns + firewall) set isBlockFree as true, so that the + // domain is resolved amd the decision is made by in flow() + makeNsOpts(transportIdsAlg(tid), true) } else if (DomainRulesManager.isDomainTrusted(fqdn)) { - // return Alg so that the decision is made by in flow() function - prepareDnsxNsOpts(Backend.Alg) + // set isBlockFree as true so that the decision is made by in flow() function + makeNsOpts(transportIdsAlg(tid), true) } else if ( DomainRulesManager.status(fqdn, UID_EVERYBODY) == DomainRulesManager.Status.BLOCK ) { - // if the domain is blocked by global rule then return block all + // if the domain is blocked by global rule then set as block all (overriding the tid) // app-wise trust is already checked above - prepareDnsxNsOpts(Backend.BlockAll) + makeNsOpts(Backend.BlockAll) + } else { + // no global rule, no app-wise trust, return the tid as it is + makeNsOpts(tid) + } + } + + private fun determineDnsTransportId(): String { + val oneWgId = WireguardManager.getOneWireGuardProxyId() + return if (oneWgId != null) { + ProxyManager.ID_WG_BASE + oneWgId + } else if (appConfig.isSystemDns() || (isAppPaused() && VpnController.isVpnLockdown())) { + // in vpn-lockdown mode+appPause , use system dns if the app is paused to mimic + // as if the apps are excluded from vpn + Backend.System } else { - // if the domain is not trusted and no app is bypassed then return preferred or - // CT+preferred so that if the domain is blocked by upstream then no need to do - // any further processing - prepareDnsxNsOpts(Backend.Preferred) + Backend.Preferred } } - private fun prepareDnsxNsOpts(userPreferredId: String): backend.DNSOpts { - val tid = transportIdForOnQuery(userPreferredId) + private fun makeNsOpts(tid: String, bypassLocalBlocklists: Boolean = false): backend.DNSOpts { val pid = proxyIdForOnQuery() val opts = backend.DNSOpts() opts.ipcsv = "" // as of now, no suggested ips - opts.tidcsv = tid + opts.tidcsv = appendDnsCacheIfNeeded(tid) opts.pid = pid + opts.noblock = bypassLocalBlocklists return opts } - private fun transportIdForOnQuery(userPreferredId: String): String { - var tid = - if (WireguardManager.oneWireGuardEnabled()) { - val wg = WireguardManager.getOneWireGuardProxyId() - val fullId = wg?.let { ProxyManager.ID_WG_BASE + wg } ?: "" - if (hasProxy(fullId)) { - fullId - } else { - "" - } - } else { - "" - } - - if (tid.isEmpty()) { - tid = - if (appConfig.isSystemDns()) { - // system dns is treated as special in GoTun2Socks, so return as Dnsx.System if - // enabled - Backend.System - } else { - userPreferredId - } - } - // case when tid and userPreferredId is Alg, then return BlockFree + Preferred - // case when tid is Alg and userPreferredId is not Alg, then return BlockFree + tid - // tid can be System / dnsProxyId - return if (userPreferredId == Backend.Alg) { + private fun transportIdsAlg(preferredId: String): String { + // case when userPreferredId is Alg, then return BlockFree + tid + // tid can be System / ProxyId / Preferred + return if (isRethinkDnsEnabled()) { val sb = StringBuilder() - val bf = appendDnsCacheIfNeeded(Backend.BlockFree) - val pref = appendDnsCacheIfNeeded(tid) - sb.append(bf).append(",").append(pref) + val tr1 = appendDnsCacheIfNeeded(Backend.BlockFree) + val tr2 = appendDnsCacheIfNeeded(preferredId) // ideally, it should be Preferred + sb.append(tr1).append(",").append(tr2) sb.toString() } else { - appendDnsCacheIfNeeded(tid) + appendDnsCacheIfNeeded(preferredId) } } + private fun isRethinkDnsEnabled(): Boolean { + return appConfig.isRethinkDnsConnected() && !WireguardManager.oneWireGuardEnabled() + } + private fun appendDnsCacheIfNeeded(id: String): String { - return if (canUseDnsCacheOnTransportId(id)) { + return if (canUseDnsCacheOnTransportId(id) && !id.startsWith(Backend.CT)) { Backend.CT + id } else { id } } - private fun hasProxy(id: String): Boolean { - if (id.isEmpty()) return false - // only add proxy id for one wireguard, no need for catchall wireguard - // catchall wireguard need not have all the apps added to it - return vpnAdapter?.getProxyById(id) != null - } - private fun canUseDnsCacheOnTransportId(userPreferredId: String): Boolean { // if userPreferredId is Dnsx.BlockAll, Alg then don't need to append CT return persistentState.enableDnsCache && userPreferredId != Backend.BlockAll } private fun proxyIdForOnQuery(): String { + // user setting to disable proxy dns + if (!persistentState.proxyDns) { + return Backend.Base + } + return if (appConfig.isCustomSocks5Enabled()) { - val id = ProxyManager.ID_S5_BASE - if (hasProxy(id)) { - id - } else { - Backend.Base - } + ProxyManager.ID_S5_BASE } else if (appConfig.isCustomHttpProxyEnabled()) { - val id = ProxyManager.ID_HTTP_BASE - if (hasProxy(id)) { - id - } else { - Backend.Base - } + ProxyManager.ID_HTTP_BASE } else if (appConfig.isWireGuardEnabled()) { // need to check if the enabled wireguard is one-wireguard // only for one-wireguard, the dns queries are proxied if (WireguardManager.oneWireGuardEnabled()) { val id = WireguardManager.getOneWireGuardProxyId() ?: return Backend.Base - val fullId = ProxyManager.ID_WG_BASE + id - if (hasProxy(fullId)) { - fullId - } else { - Backend.Base - } + ProxyManager.ID_WG_BASE + id } else if (WireguardManager.catchAllEnabled()) { // if the enabled wireguard is catchall-wireguard, then return wireguard id val id = WireguardManager.getCatchAllWireGuardProxyId() ?: return Backend.Base - val fullId = ProxyManager.ID_WG_BASE + id - if (hasProxy(fullId)) { - fullId - } else { - Backend.Base - } + ProxyManager.ID_WG_BASE + id } else { // if the enabled wireguard is not one-wireguard, then return base Backend.Base @@ -2506,7 +2637,49 @@ class BraveVPNService : netLogTracker.processDnsLog(summary) } - override fun onComplete(p0: ServerSummary?) { + override fun onProxiesStopped() { + // proxies stopped will happen only when the vpn is stopped so, no need to restart the vpn + // just clear the active config timestamps + WireguardManager.clearActiveConfigTimestamps() + } + + override fun onProxyAdded(id: String) { + if (!id.contains(ProxyManager.ID_WG_BASE)) { + // only wireguard proxies are considered for overlay network + return + } + WireguardManager.setActiveConfigTimestamp(id, elapsedRealtime()) + // new proxy added, refresh overlay network pair + val nw: OverlayNetworks? = vpnAdapter?.getActiveProxiesIpVersion() + logd("onProxyAdded for proxy $id: $nw") + onOverlayNetworkChanged(nw ?: OverlayNetworks()) + } + + override fun onProxyRemoved(id: String) { + if (!id.contains(ProxyManager.ID_WG_BASE)) { + // only wireguard proxies are considered for overlay network + return + } + WireguardManager.removeActiveConfigTimestamp(id) + // proxy removed, refresh overlay network pair + val nw: OverlayNetworks? = vpnAdapter?.getActiveProxiesIpVersion() + logd("onProxyRemoved for proxy $id: $nw") + onOverlayNetworkChanged(nw ?: OverlayNetworks()) + } + + override fun onDNSAdded(id: String) { + // no-op + } + + override fun onDNSRemoved(id: String) { + // no-op + } + + override fun onDNSStopped() { + // no-op + } + + override fun onComplete(p0: ServerSummary) { // no-op } @@ -2637,6 +2810,15 @@ class BraveVPNService : ) val trapVpnDns = isDns(dstPort) && isVpnDns(dstIp) + val trapVpnPrivateDns = isVpnDns(dstIp) && isPrivateDns(dstPort) + + // always block, since the vpn tunnel doesn't serve dns-over-tls + if (trapVpnPrivateDns) { + logd("flow: dns-over-tls, returning Ipn.Block, $uid") + cm.isBlocked = true + cm.blockedByRule = FirewallRuleset.RULE1C.id + return@runBlocking persistAndConstructFlowResponse(cm, Backend.Block, connId, uid) + } val isRethink = uid == rethinkUid if (isRethink) { @@ -2644,7 +2826,7 @@ class BraveVPNService : logd( "flow: Ipn.Exit for rethink, $uid, $packageName, $srcIp, $srcPort, $realDestIp, $dstPort, $possibleDomains" ) - if (domains.isEmpty()) { + if (cm.query.isNullOrEmpty()) { // possible domains only used for logging purposes, it may be available if // the domains are empty. So, use the possibleDomains only if domains is empty // no need to show the possible domains other than rethink @@ -2667,6 +2849,7 @@ class BraveVPNService : // connection is closed (onSocketClosed), use: ui to show the active connections val key = CidKey(cm.connId, uid) trackedCids.add(key) + return@runBlocking persistAndConstructFlowResponse(cm, proxy, connId, uid, isRethink) } @@ -2697,64 +2880,39 @@ class BraveVPNService : uid: Int = connTracker.uid ): intra.Mark { // check for one-wireguard, if enabled, return wireguard proxy for all connections - if (WireguardManager.oneWireGuardEnabled()) { - logd("flow: one-wireguard is enabled init, $connId, $uid") - val id = - WireguardManager.getOneWireGuardProxyId() - ?: return persistAndConstructFlowResponse( - connTracker, - Backend.Base, - connId, - uid - ) - val proxyId = "${ProxyManager.ID_WG_BASE}${id}" - logd( - "flow: one-wireguard is enabled, returning $proxyId, ${WireguardManager.canRouteIp(id, connTracker.destIP)}, ${hasProxy(proxyId)}" - ) - if (WireguardManager.canRouteIp(id, connTracker.destIP) && hasProxy(proxyId)) { - logd("flow: one-wireguard is enabled, returning $proxyId, $connId, $uid") - return persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) + val oneWgId = WireguardManager.getOneWireGuardProxyId() + if (oneWgId != null && oneWgId != WireguardManager.INVALID_CONF_ID) { + val proxyId = "${ProxyManager.ID_WG_BASE}${oneWgId}" + // regardless of whether this proxyId exists in go, use it to avoid leaks + val canRoute = vpnAdapter?.canRouteIp(proxyId, connTracker.destIP, true) + return if (canRoute == true) { + logd("flow: one-wg is enabled, returning $proxyId, $connId, $uid") + persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) } else { // in some configurations the allowed ips will not be 0.0.0.0/0, so the connection // will be dropped, in those cases, return base (connection will be forwarded to // base proxy) - logd( - "flow: one-wireguard is enabled, but no route/proxy available, returning Ipn.Base, $connId, $uid" - ) - return persistAndConstructFlowResponse(connTracker, Backend.Base, connId, uid) - } - } - - // before checking for other proxy, check for wireguard, as it has lockdown scenario - // do not check for enabled for lockdown scenario - val cf = WireguardManager.getConfigIdForApp(uid) - val proxyId = "${ProxyManager.ID_WG_BASE}${cf?.id}" - // if no config is assigned / enabled for this app, pass-through - // add ID_WG_BASE to the id to get the proxyId - if (cf == null || cf.id == WireguardManager.INVALID_CONF_ID) { - // wireguard is not enabled for this app, pass-through - logd("flow: wireguard is not enabled for this app, pass-through, $connId, $uid") - } else if (!cf.isActive) { - // app is included in the config, but the config is not active check for lockdown - if (cf.isLockdown || cf.isCatchAll) { - logd( - "flow: wireguard is not enabled for this app, but lockdown/catch-all is enabled, returning $proxyId, $connId, $uid" - ) - return persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) - } else { - logd( - "flow: wireguard is enabled but app is not included, proceed for other checks, $connId, $uid" - ) - // pass-through, no wireguard config is enabled for this app + logd("flow: one-wg is enabled, but no route; ret:Ipn.Base, $connId, $uid") + persistAndConstructFlowResponse(connTracker, Backend.Base, connId, uid) } - } else { - logd( - "flow: wireguard is enabled and app is included, returning $proxyId, $connId, $uid" - ) - return if (hasProxy(proxyId)) { - persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) + } + + val wgConfig = WireguardManager.getConfigIdForApp(uid) // also accounts for catch-all + if (wgConfig != null && wgConfig.id != WireguardManager.INVALID_CONF_ID) { + val proxyId = "${ProxyManager.ID_WG_BASE}${wgConfig.id}" + // even if inactive, route connections to wg if lockdown/catch-all is enabled to + // avoid leaks + return if (wgConfig.isActive || wgConfig.isLockdown || wgConfig.isCatchAll) { + val canRoute = vpnAdapter?.canRouteIp(proxyId, connTracker.destIP, true) + if (canRoute == true) { + logd("flow: wg is active/lockdown/catch-all; $proxyId, $connId, $uid") + persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) + } else { + logd("flow: wg is active/lockdown/catch-all, but no route, $connId, $uid") + persistAndConstructFlowResponse(connTracker, Backend.Base, connId, uid) + } } else { - // in some configurations the allowed ips will not be + logd("flow: wg is not active; using base, $connId, $uid") persistAndConstructFlowResponse(connTracker, Backend.Base, connId, uid) } } @@ -2806,14 +2964,12 @@ class BraveVPNService : } }*/ - if (appConfig.isOrbotProxyEnabled() && hasProxy(ProxyManager.ID_ORBOT_BASE)) { + if (appConfig.isOrbotProxyEnabled()) { val endpoint = appConfig.getConnectedOrbotProxy() val packageName = FirewallManager.getPackageNameByUid(uid) if (endpoint?.proxyAppName == packageName) { - logd( - "flow: orbot proxy is enabled and app is $packageName, returning ${Backend.Exit}, $connId, $uid" - ) - connTracker.isBlocked = false + logd("flow: orbot exit for $packageName, $connId, $uid") + connTracker.isBlocked = false // do not block orbot, in case of orbot proxy connTracker.blockedByRule = FirewallRuleset.RULE0.id return persistAndConstructFlowResponse(connTracker, Backend.Exit, connId, uid) } @@ -2823,9 +2979,7 @@ class BraveVPNService : Log.e(LOG_TAG_VPN, "flow: orbot proxy is enabled but app is not included") // pass-through } else { - logd( - "flow: received rule: orbot, returning ${ProxyManager.ID_ORBOT_BASE}, $connId, $uid" - ) + logd("flow: orbot proxy for $uid, $connId") return persistAndConstructFlowResponse( connTracker, ProxyManager.ID_ORBOT_BASE, @@ -2836,20 +2990,19 @@ class BraveVPNService : } // chose socks5 proxy over http proxy - if (appConfig.isCustomSocks5Enabled() && hasProxy(ProxyManager.ID_S5_BASE)) { + if (appConfig.isCustomSocks5Enabled()) { val endpoint = runBlocking { appConfig.getSocks5ProxyDetails() } val packageName = FirewallManager.getPackageNameByUid(uid) logd("flow: socks5 proxy is enabled, $packageName, ${endpoint.proxyAppName}") + // do not block the app if the app is set to forward the traffic via socks5 proxy if (endpoint.proxyAppName == packageName) { - logd( - "flow: socks5 proxy is enabled and app is $packageName, returning ${Backend.Exit}, $connId, $uid" - ) + logd("flow: socks5 exit for $packageName, $connId, $uid") connTracker.isBlocked = false connTracker.blockedByRule = FirewallRuleset.RULE0.id return persistAndConstructFlowResponse(connTracker, Backend.Exit, connId, uid) } - logd("flow: rule: socks5, use ${ProxyManager.ID_S5_BASE}, $connId, $uid") + logd("flow: socks5 proxy for $connId, $uid") return persistAndConstructFlowResponse( connTracker, ProxyManager.ID_S5_BASE, @@ -2858,19 +3011,18 @@ class BraveVPNService : ) } - if (appConfig.isCustomHttpProxyEnabled() && hasProxy(ProxyManager.ID_HTTP_BASE)) { + if (appConfig.isCustomHttpProxyEnabled()) { val endpoint = runBlocking { appConfig.getHttpProxyDetails() } val packageName = FirewallManager.getPackageNameByUid(uid) + // do not block the app if the app is set to forward the traffic via http proxy if (endpoint.proxyAppName == packageName) { - logd( - "flow: http proxy is enabled and app is $packageName, returning ${Backend.Exit}, $connId, $uid" - ) + logd("flow: http exit for $packageName, $connId, $uid") connTracker.isBlocked = false connTracker.blockedByRule = FirewallRuleset.RULE0.id return persistAndConstructFlowResponse(connTracker, Backend.Exit, connId, uid) } - logd("flow: rule: http, use ${ProxyManager.ID_HTTP_BASE}, $connId, $uid") + logd("flow: http proxy for $connId, $uid") return persistAndConstructFlowResponse( connTracker, ProxyManager.ID_HTTP_BASE, @@ -2879,7 +3031,7 @@ class BraveVPNService : ) } - logd("flow: no proxy enabled, returning Ipn.Base, $connId, $uid") + logd("flow: no proxies, Ipn.Base, $connId, $uid") return persistAndConstructFlowResponse(connTracker, Backend.Base, connId, uid) } @@ -2905,12 +3057,6 @@ class BraveVPNService : io("refreshWg") { vpnAdapter?.refreshProxies() } } - fun updateWireGuardConfig() { - logd("update wg config") - val tunProxyMode = appConfig.getTunProxyMode() - io("updateWg") { vpnAdapter?.setWireguardTunnelModeIfNeeded(tunProxyMode) } - } - fun getDnsStatus(id: String): Long? { return vpnAdapter?.getDnsStatus(id) } @@ -2920,25 +3066,25 @@ class BraveVPNService : } private fun persistAndConstructFlowResponse( - connTracker: ConnTrackerMetaData?, + cm: ConnTrackerMetaData?, proxyId: String, connId: String, uid: Int, isRethink: Boolean = false ): intra.Mark { - // persist the connTracker - if (connTracker != null) { - connTracker.proxyDetails = proxyId - // assign the proxy details to the connTracker after the decision is made - if (ProxyManager.isIpnProxy(proxyId) && !connTracker.isBlocked) { - connTracker.blockedByRule = FirewallRuleset.RULE12.id + // persist ConnTrackerMetaData + if (cm != null) { + cm.proxyDetails = proxyId + // assign the proxy details to cm after the decision is made + if (ProxyManager.isIpnProxy(proxyId) && !cm.isBlocked) { + cm.blockedByRule = FirewallRuleset.RULE12.id } if (isRethink) { - netLogTracker.writeRethinkLog(connTracker) + netLogTracker.writeRethinkLog(cm) } else { - netLogTracker.writeIpLog(connTracker) + netLogTracker.writeIpLog(cm) } - logd("flow: connTracker: $connTracker") + logd("flow: connTracker: $cm") } val mark = intra.Mark() @@ -2950,6 +3096,14 @@ class BraveVPNService : } else { mark.uid = uid.toString() } + if (cm == null) { + logd("flow: returning mark: $mark for connId: $connId, uid: $uid, cm: null") + } else { + Log.i( + LOG_TAG_VPN, + "flow: returning mark: $mark for src(${cm.sourceIP}: ${cm.sourcePort}), dest(${cm.destIP}:${cm.destPort})" + ) + } return mark } @@ -3021,6 +3175,19 @@ class BraveVPNService : } } + fun getSupportedIpVersion(id: String): Pair? { + return if (vpnAdapter != null) { + vpnAdapter?.getSupportedIpVersion(id) + } else { + Log.w(LOG_TAG_VPN, "error while fetching protocol status: vpnAdapter is null") + null + } + } + + fun isSplitTunnelProxy(id: String, pair: Pair): Boolean { + return vpnAdapter?.isSplitTunnelProxy(id, pair) ?: false + } + fun syncP50Latency(id: String) { io("syncP50Latency") { vpnAdapter?.syncP50Latency(id) } } From 190579ea06ef00f61a6d7a773c9fe38ef5bc7a68 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Fri, 29 Mar 2024 13:47:14 +0530 Subject: [PATCH 57/81] bump firestack for v055d --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 567e50606..70f6cfa1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,8 +247,8 @@ dependencies { fullImplementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9' // from: https://jitpack.io/#celzero/firestack - download 'com.github.celzero:firestack:b29ea51b53@aar' - implementation 'com.github.celzero:firestack:b29ea51b53@aar' + download 'com.github.celzero:firestack:cb7d467a74@aar' + implementation 'com.github.celzero:firestack:cb7d467a74@aar' // Work manager implementation('androidx.work:work-runtime-ktx:2.9.0') { From a4e008d7fea23cd03c9eb933c93315406ac91d4d Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Fri, 29 Mar 2024 16:53:38 +0530 Subject: [PATCH 58/81] log level from error to warn --- .../java/com/celzero/bravedns/scheduler/BugReportZipper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt b/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt index 4b685c8d3..0354964bd 100644 --- a/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt +++ b/app/src/full/java/com/celzero/bravedns/scheduler/BugReportZipper.kt @@ -80,10 +80,10 @@ object BugReportZipper { return try { ZipFile(getZipFileName(dir)) } catch (e: FileNotFoundException) { - Log.e(LOG_TAG_SCHEDULER, "File not found exception while creating zip file", e) + Log.w(LOG_TAG_SCHEDULER, "File not found exception while creating zip file", e) null } catch (e: ZipException) { - Log.e(LOG_TAG_SCHEDULER, "Zip exception while creating zip file", e) + Log.w(LOG_TAG_SCHEDULER, "Zip exception while creating zip file", e) null } } From ab8bcf842f6176f5782dd12109e23bf08ab1b148 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sat, 30 Mar 2024 15:21:26 +0530 Subject: [PATCH 59/81] ui: update heading and positive btn for delete dialog --- .../com/celzero/bravedns/adapter/WgPeersAdapter.kt | 13 +++++++++---- .../bravedns/ui/activity/WgConfigDetailActivity.kt | 10 ++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt index 49cb95593..838702dd1 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgPeersAdapter.kt @@ -112,12 +112,17 @@ class WgPeersAdapter( private fun showDeleteInterfaceDialog(wgPeer: Peer) { val builder = MaterialAlertDialogBuilder(context) - builder.setTitle(context.getString(R.string.config_delete_dialog_title)) + val delText = + context.getString( + R.string.two_argument_space, + context.getString(R.string.config_delete_dialog_title), + context.getString(R.string.lbl_peer) + ) + builder.setTitle(delText) builder.setMessage(context.getString(R.string.config_delete_dialog_desc)) builder.setCancelable(true) - builder.setPositiveButton(context.getString(R.string.lbl_delete)) { _, _ -> - deletePeer(wgPeer) - } + + builder.setPositiveButton(delText) { _, _ -> deletePeer(wgPeer) } builder.setNegativeButton(context.getString(R.string.lbl_cancel)) { _, _ -> // no-op diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt index 8976228eb..de6d33a9d 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt @@ -400,10 +400,16 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { private fun showDeleteInterfaceDialog() { val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(getString(R.string.lbl_delete)) + val delText = + getString( + R.string.two_argument_space, + getString(R.string.config_delete_dialog_title), + getString(R.string.lbl_wireguard) + ) + builder.setTitle(delText) builder.setMessage(getString(R.string.config_delete_dialog_desc)) builder.setCancelable(true) - builder.setPositiveButton(this.getString(R.string.lbl_delete)) { _, _ -> + builder.setPositiveButton(delText) { _, _ -> io { WireguardManager.deleteConfig(configId) uiCtx { From f103303ecf926556c8215b0165556c18cd0bfa0e Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sat, 30 Mar 2024 15:26:45 +0530 Subject: [PATCH 60/81] Implement DNS and network configuration enhancements - rmv loopback from default/system dns - add routes along with setLink - fetch dns servers from connection monitor - bind dns ips with appropriate network - improvements on network changes check --- .../com/celzero/bravedns/data/AppConfig.kt | 6 +- .../celzero/bravedns/net/go/GoVpnAdapter.kt | 24 +- .../bravedns/service/BraveVPNService.kt | 299 +++++++++++------- .../bravedns/service/ConnectionMonitor.kt | 31 +- .../bravedns/service/IpRulesManager.kt | 2 +- .../celzero/bravedns/util/InternetProtocol.kt | 9 + 6 files changed, 223 insertions(+), 148 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt b/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt index 0ffd74d4c..da6d5ad7c 100644 --- a/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt +++ b/app/src/main/java/com/celzero/bravedns/data/AppConfig.kt @@ -75,8 +75,7 @@ internal constructor( private const val ORBOT_DNS = "Orbot" - const val LOOPBACK_DNS = "localhost" // also default fallback ip - const val RINR_FALLBACK_DNS = "8.8.4.4" + const val FALLBACK_DNS = "8.8.4.4" } init { @@ -94,7 +93,6 @@ internal constructor( val bridge: Bridge, val defaultDns: String, val fakeDns: String, - val preferredEngine: InternetProtocol, val mtu: Int ) @@ -578,7 +576,6 @@ internal constructor( fun newTunnelOptions( bridge: Bridge, fakeDns: String, - preferredEngine: InternetProtocol, ptMode: ProtoTranslationMode, mtu: Int ): TunnelOptions { @@ -590,7 +587,6 @@ internal constructor( bridge, getDefaultDns(), fakeDns, - preferredEngine, mtu ) } diff --git a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt index 467a036b6..81d15ca21 100644 --- a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt +++ b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt @@ -25,8 +25,7 @@ import backend.Backend import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.data.AppConfig -import com.celzero.bravedns.data.AppConfig.Companion.LOOPBACK_DNS -import com.celzero.bravedns.data.AppConfig.Companion.RINR_FALLBACK_DNS +import com.celzero.bravedns.data.AppConfig.Companion.FALLBACK_DNS import com.celzero.bravedns.data.AppConfig.TunnelOptions import com.celzero.bravedns.database.ProxyEndpoint import com.celzero.bravedns.service.BraveVPNService @@ -90,7 +89,6 @@ class GoVpnAdapter : KoinComponent { Intra.connect( tunFd.fd.toLong(), opts.mtu.toLong(), - opts.preferredEngine.value(), opts.fakeDns, defaultDns, opts.bridge @@ -798,7 +796,7 @@ class GoVpnAdapter : KoinComponent { } private fun newDefaultTransport(url: String): intra.DefaultDNS { - val defaultDns = getLoopbackOrFallbackDns() + val defaultDns = FALLBACK_DNS try { // when the url is empty, set the default transport to 8.8.4.4:53 if (url.isEmpty()) { @@ -820,18 +818,8 @@ class GoVpnAdapter : KoinComponent { } } - private fun getLoopbackOrFallbackDns(): String { - // in rinr loopback mode, the loopback dns is send back to the tunnel this will result in - // infinite loop, so use RINR_FALLBACK_DNS instead of LOOPBACK_DNS - return if (persistentState.routeRethinkInRethink) { - RINR_FALLBACK_DNS - } else { - LOOPBACK_DNS - } - } - fun addDefaultTransport(url: String?) { - val defaultDns = getLoopbackOrFallbackDns() + val defaultDns = FALLBACK_DNS try { if (!tunnel.isConnected) { Log.i(LOG_TAG_VPN, "Tunnel not connected, skip new default transport") @@ -880,18 +868,18 @@ class GoVpnAdapter : KoinComponent { } catch (e: Exception) { // this is not expected to happen Log.e(LOG_TAG_VPN, "set system dns: could not parse system dns", e) // see BraveVpnService#determineSystemDns() - Intra.setSystemDNS(tunnel, LOOPBACK_DNS) + Intra.setSystemDNS(tunnel, FALLBACK_DNS) } } - suspend fun updateLink(tunFd: ParcelFileDescriptor, opts: TunnelOptions): Boolean { + suspend fun updateLinkAndRoutes(tunFd: ParcelFileDescriptor, opts: TunnelOptions, proto: Long): Boolean { if (!tunnel.isConnected) { Log.e(LOG_TAG_VPN, "updateLink: tunFd is null, returning") return false } Log.i(LOG_TAG_VPN, "updateLink with fd(${tunFd.fd}) mtu: ${opts.mtu}") return try { - tunnel.setLink(tunFd.fd.toLong(), opts.mtu.toLong()) + tunnel.setLinkAndRoutes(tunFd.fd.toLong(), opts.mtu.toLong(), proto) true } catch (e: Exception) { Log.e(LOG_TAG_VPN, "err update tun: ${e.message}", e) diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index f4834c0da..215a847f5 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -35,7 +35,6 @@ import android.net.VpnService import android.os.Build.VERSION import android.os.Build.VERSION_CODES import android.os.ParcelFileDescriptor -import android.os.SystemClock import android.os.SystemClock.elapsedRealtime import android.system.OsConstants.AF_INET import android.system.OsConstants.AF_INET6 @@ -53,7 +52,7 @@ import backend.RDNS import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.data.AppConfig -import com.celzero.bravedns.data.AppConfig.Companion.LOOPBACK_DNS +import com.celzero.bravedns.data.AppConfig.Companion.FALLBACK_DNS import com.celzero.bravedns.data.ConnTrackerMetaData import com.celzero.bravedns.data.ConnectionSummary import com.celzero.bravedns.database.AppInfo @@ -224,10 +223,10 @@ class BraveVPNService : if (DEBUG) Log.d(LOG_TAG_VPN, msg) } - override fun bind4(who: String, fid: Long) { + override fun bind4(who: String, addrPort: String, fid: Long) { val rinr = persistentState.routeRethinkInRethink val curnet = underlyingNetworks - logd("protect: $who, $fid, rinr? $rinr") + logd("bind4: $who, $fid, rinr? $rinr") if (rinr && who != Backend.Exit) { // do not proceed if rethink within rethink is enabled and proxyId(who) is not exit return @@ -242,27 +241,54 @@ class BraveVPNService : } var pfd: ParcelFileDescriptor? = null - // allNet, ipv4Net, ipv6Net is always sorted, first network is always the active network - curnet?.ipv4Net?.forEach { - try { - pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) - - it.network.bindSocket(pfd!!.fileDescriptor) - logd("bind4: $who, ${it.network.networkHandle}, ${it.networkType}") + try { + // split the addrPort to get the IP address and convert it to InetAddress + val destIp = IPAddressString(IpRulesManager.splitHostPort(addrPort).first).address + // in case of zero, bind only for wg connections, wireguard tries to bind to + // network with zero addresses + if ( + (destIp.isZero && who.contains(ProxyManager.ID_WG_BASE)) || + destIp.isAnyLocal || + destIp.isLoopback + ) { + logd("bind4: invalid destIp: $destIp, who: $who, $addrPort") return - } catch (e: IOException) { - Log.e(LOG_TAG_VPN, "err bind4 for fid: $fid, ${e.message}, $e") - } finally { - pfd?.detachFd() } + val destAddr = destIp.toInetAddress() + pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) + val net = curnet?.dnsServers?.get(destAddr) + if (net != null) { + try { + logd("bind4: $who, $addrPort, $fid, ${net.networkHandle}") + net.bindSocket(pfd!!.fileDescriptor) + return + } catch (e: IOException) { + Log.e(LOG_TAG_VPN, "err bind4 for fid: $fid, ${e.message}, $e") + } + } else { + curnet?.ipv4Net?.forEach { + try { + it.network.bindSocket(pfd!!.fileDescriptor) + logd( + "bind4: $who, $addrPort, ${it.network.networkHandle}, ${it.networkType}" + ) + return + } catch (e: IOException) { + Log.e(LOG_TAG_VPN, "err bind4 for fid: $fid, ${e.message}, $e") + } + } + } + } finally { + pfd?.detachFd() } - logd("bind4: no network to bind, ${curnet?.ipv4Net?.size}") + + logd("bind4: no network to bind, ${curnet?.dnsServers?.keys}, who: $who, $addrPort") } - override fun bind6(who: String, fid: Long) { + override fun bind6(who: String, addrPort: String, fid: Long) { val rinr = persistentState.routeRethinkInRethink val curnet = underlyingNetworks - logd("protect: $who, $fid, rinr? $rinr") + logd("bind6: $who, $fid, rinr? $rinr") if (rinr && who != Backend.Exit) { // do not proceed if rethink within rethink is enabled and proxyId(who) is not exit return @@ -278,19 +304,37 @@ class BraveVPNService : } var pfd: ParcelFileDescriptor? = null - curnet?.ipv6Net?.forEach { - try { - pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) - it.network.bindSocket(pfd!!.fileDescriptor) - logd("bind6: $who, ${it.network.networkHandle}, ${it.networkType}") - return - } catch (e: IOException) { - Log.e(LOG_TAG_VPN, "err(bind6) fid: $fid, ${e.message}, $e") - } finally { - pfd?.detachFd() + try { + // split the addrPort to get the IP address and convert it to InetAddress + val destAddr = + IPAddressString(IpRulesManager.splitHostPort(addrPort).first) + .address + .toInetAddress() + pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) + val net = curnet?.dnsServers?.get(destAddr) + if (net != null) { + try { + logd("bind6: $who, $addrPort, $fid, ${net.networkHandle}") + net.bindSocket(pfd!!.fileDescriptor) + return + } catch (e: IOException) { + Log.e(LOG_TAG_VPN, "err bind6 for fid: $fid, ${e.message}, $e") + } + } else { + curnet?.ipv6Net?.forEach { + try { + it.network.bindSocket(pfd!!.fileDescriptor) + logd("bind6: $who, ${it.network.networkHandle}, ${it.networkType}") + return + } catch (e: IOException) { + Log.e(LOG_TAG_VPN, "err bind6 for fid: $fid, ${e.message}, $e") + } + } } + } finally { + pfd?.detachFd() } - logd("bind6: no network to bind, ${curnet?.ipv6Net?.size}") + logd("bind6: no network to bind, ${curnet?.dnsServers?.keys}, who: $who, $addrPort") } override fun protect(who: String?, fd: Long) { @@ -1038,7 +1082,7 @@ class BraveVPNService : Log.i(LOG_TAG_VPN, "excluded-apps list changed, restart vpn") - io("excludeApps") { restartVpnWithNewAppConfig() } + io("excludeApps") { restartVpnWithNewAppConfig(reason = "excludeApps") } } catch (e: Exception) { // NoSuchElementException, ConcurrentModification Log.e(LOG_TAG_VPN, "error retrieving value from appInfos observer ${e.message}", e) } @@ -1252,7 +1296,6 @@ class BraveVPNService : appConfig.newTunnelOptions( this, getFakeDns(), - appConfig.getInternetProtocol(), appConfig.getProtocolTranslationMode(), mtu ) @@ -1271,7 +1314,7 @@ class BraveVPNService : // refresh should happen before restartVpn, otherwise the new vpn will not // have app, ip, domain rules. See RefreshDatabase#refresh rdb.refresh(RefreshDatabase.ACTION_REFRESH_AUTO) { - restartVpn(opts, Networks(null, overlayNetworks)) + restartVpn(opts, Networks(null, overlayNetworks), why = "startVpn") controllerProto = Pair(overlayNetworks.has4, overlayNetworks.has6) // update the controller, which will update the UI (home screen btm sheet) VpnController.updateProtocol(controllerProto) @@ -1435,7 +1478,7 @@ class BraveVPNService : io("updateDnscrypt") { addTransport() } } PersistentState.ALLOW_BYPASS -> { - io("allowBypass") { restartVpnWithNewAppConfig() } + io("allowBypass") { restartVpnWithNewAppConfig(reason = "allowBypass") } } PersistentState.PROXY_TYPE -> { io("proxy") { @@ -1473,11 +1516,13 @@ class BraveVPNService : } PersistentState.PRIVATE_IPS -> { // restart vpn to enable/disable route lan traffic - io("routeLanTraffic") { restartVpnWithNewAppConfig() } + io("routeLanTraffic") { restartVpnWithNewAppConfig(reason = "routeLanTraffic") } } PersistentState.RETHINK_IN_RETHINK -> { // restart vpn to allow/disallow rethink traffic in rethink - io("routeRethinkInRethink") { restartVpnWithNewAppConfig() } + io("routeRethinkInRethink") { + restartVpnWithNewAppConfig(reason = "routeRethinkInRethink") + } } } } @@ -1531,7 +1576,7 @@ class BraveVPNService : // route changes as informed by the connection monitor notifyConnectionMonitor() } - restartVpnWithNewAppConfig() + restartVpnWithNewAppConfig(reason = "handleIPProtoChanges") setRoute() } @@ -1593,7 +1638,8 @@ class BraveVPNService : private suspend fun restartVpnWithNewAppConfig( underlyingNws: ConnectionMonitor.UnderlyingNetworks? = underlyingNetworks, - overlayNws: OverlayNetworks = overlayNetworks + overlayNws: OverlayNetworks = overlayNetworks, + reason: String ) { logd("restart vpn with new app config") val nws = Networks(underlyingNws, overlayNws) @@ -1601,11 +1647,11 @@ class BraveVPNService : appConfig.newTunnelOptions( this, getFakeDns(), - appConfig.getInternetProtocol(), appConfig.getProtocolTranslationMode(), mtu() ), - nws + nws, + reason ) // update the controller, which will update the UI (home screen btm sheet) VpnController.updateProtocol(controllerProto) @@ -1626,7 +1672,6 @@ class BraveVPNService : appConfig.newTunnelOptions( this, getFakeDns(), - appConfig.getInternetProtocol(), appConfig.getProtocolTranslationMode(), mtu() ) @@ -1637,14 +1682,15 @@ class BraveVPNService : vpnAdapter?.setTunMode(tunnelOptions) } - private suspend fun restartVpn(opts: AppConfig.TunnelOptions, networks: Networks) { - var tunFd: ParcelFileDescriptor? = null + private suspend fun restartVpn(opts: AppConfig.TunnelOptions, networks: Networks, why: String) { + val tunFd: ParcelFileDescriptor? + var proto = InternetProtocol.IPv46.value() VpnController.mutex.withLock { // connectionMonitor = null indicates onStartCommand has not yet been called if (connectionMonitor == null) { - Log.e( - LOG_TAG_VPN, - "cannot restart-vpn, conn monitor null! Was onStartCommand called?" + logAndToastIfNeeded( + "$why, cannot restart-vpn, conn monitor null! Was onStartCommand called?", + Log.ERROR ) return } @@ -1653,33 +1699,45 @@ class BraveVPNService : // when persistent-state "thinks" vpn is disabled, stop the service, especially when // we could be here via onStartCommand -> isNewVpn -> restartVpn while both, // vpn-service & conn-monitor exists & vpn-enabled state goes out of sync - Log.e( - LOG_TAG_VPN, - "stop-vpn(restartVpn), tracking vpn is out of sync", - java.lang.RuntimeException() + logAndToastIfNeeded( + "$why, stop-vpn(restartVpn), tracking vpn is out of sync", + Log.ERROR ) io("outOfSyncRestart") { signalStopService(userInitiated = false) } return } // attempt seamless hand-off as described in VpnService.Builder.establish() docs - tunFd = establishVpn(networks) + val es = establishVpn(networks) + tunFd = es.first + proto = es.second if (tunFd == null) { - Log.e(LOG_TAG_VPN, "cannot restart-vpn, no tun-fd") + logAndToastIfNeeded("$why, cannot restart-vpn, no tun-fd", Log.ERROR) io("noTunRestart") { signalStopService(userInitiated = false) } return } - val ok = makeOrUpdateVpnAdapter(tunFd, opts) - Log.i(LOG_TAG_VPN, "restartVpn? ${vpnAdapter != null}, done? $ok") + val ok = makeOrUpdateVpnAdapter(tunFd, opts, proto) if (!ok) { - Log.w(LOG_TAG_VPN, "cannot handle vpn adapter changes, no tunnel") + logAndToastIfNeeded("$why, cannot restart-vpn, no vpn-adapter", Log.ERROR) io("noTunnelRestart") { signalStopService(userInitiated = false) } return - } // else: tunnel is up, fall through + } else { + logAndToastIfNeeded("$why, vpn restarted", Log.INFO) + } notifyConnectionStateChangeIfNeeded() } + private suspend fun logAndToastIfNeeded(msg: String, logLevel: Int = Log.WARN) { + when (logLevel) { + Log.WARN -> Log.w(LOG_TAG_VPN, msg) + Log.ERROR -> Log.e(LOG_TAG_VPN, msg) + Log.INFO -> Log.i(LOG_TAG_VPN, msg) + else -> Log.d(LOG_TAG_VPN, msg) + } + uiCtx("toast") { if (DEBUG) showToastUiCentered(this, msg, Toast.LENGTH_LONG) } + } + private fun notifyConnectionStateChangeIfNeeded() { // Case: Set state to working in case of Firewall mode if (appConfig.getBraveMode().isFirewallMode()) { @@ -1703,7 +1761,8 @@ class BraveVPNService : private suspend fun makeOrUpdateVpnAdapter( tunFd: ParcelFileDescriptor, - opts: AppConfig.TunnelOptions + opts: AppConfig.TunnelOptions, + proto: Long ): Boolean { val ok = true val noTun = false // should eventually call signalStopService(userInitiated=false) @@ -1712,7 +1771,7 @@ class BraveVPNService : if (vpnAdapter != null) { Log.i(LOG_TAG_VPN, "vpn-adapter exists, use it") // in case, if vpn-adapter exists, update the existing vpn-adapter - if (vpnAdapter?.updateLink(tunFd, opts) == false) { + if (vpnAdapter?.updateLinkAndRoutes(tunFd, opts, proto) == false) { Log.e(LOG_TAG_VPN, "err update vpn-adapter") return noTun } @@ -1746,7 +1805,7 @@ class BraveVPNService : } else { setUnderlyingNetworks(emptyArray()) // setting empty array is not enough, do not add routes to the tunnel - io("nwDisconnect") { restartVpnWithNewAppConfig(networks) } + io("nwDisconnect") { restartVpnWithNewAppConfig(networks, reason = "nwDisconnect") } } setNetworkAndDefaultDnsIfNeeded() VpnController.onConnectionStateChanged(null) @@ -1757,22 +1816,21 @@ class BraveVPNService : signalStopService(userInitiated = false) } - override fun onNetworkConnected(newNetworks: ConnectionMonitor.UnderlyingNetworks) { + override fun onNetworkConnected(networks: ConnectionMonitor.UnderlyingNetworks) { val curnet = underlyingNetworks - val out = interestingNetworkChanges(curnet, newNetworks) - val isRoutesChanged = out.routesChanged - val activeRoutesChanged = out.activeRoutesChanged + val out = interestingNetworkChanges(curnet, networks) + val isRoutesChanged = hasRouteChangedInAutoMode(out) val isBoundNetworksChanged = out.netChanged val isMtuChanged = out.mtuChanged - underlyingNetworks = newNetworks - Log.i(LOG_TAG_VPN, "onNetworkConnected: changes: $out for new: $newNetworks") + underlyingNetworks = networks + Log.i(LOG_TAG_VPN, "onNetworkConnected: changes: $out for new: $networks") // always reset the system dns server ip of the active network with the tunnel setNetworkAndDefaultDnsIfNeeded() - if (newNetworks.useActive) { + if (networks.useActive) { setUnderlyingNetworks(null) - } else if (newNetworks.ipv4Net.isEmpty() && newNetworks.ipv6Net.isEmpty()) { + } else if (networks.ipv4Net.isEmpty() && networks.ipv6Net.isEmpty()) { Log.w(LOG_TAG_VPN, "network changed but empty ipv4/ipv6 networks w connectivity") if (FAIL_OPEN_ON_NO_NETWORK) { setUnderlyingNetworks(null) @@ -1782,13 +1840,21 @@ class BraveVPNService : } else { // add ipv4/ipv6 networks to the tunnel val allNetworks = - newNetworks.ipv4Net.map { it.network } + newNetworks.ipv6Net.map { it.network } + networks.ipv4Net.map { it.network } + networks.ipv6Net.map { it.network } setUnderlyingNetworks(allNetworks.toTypedArray()) } // restart vpn if the routes (+ auto mode) or when mtu changes - if (isMtuChanged || isRouteChanged(out, underlyingNetworks?.useActive == true)) { - logd("mtu $isMtuChanged or routes/active routes $isRoutesChanged/$activeRoutesChanged changed, restart vpn") - io("nwConnect") { restartVpnWithNewAppConfig(newNetworks) } + if (isMtuChanged || isRoutesChanged) { + logd( + "mtu? $isMtuChanged(o:${curnet?.minMtu}, n:${networks.minMtu}); routes? $isRoutesChanged, restart vpn" + ) + io("nwConnect") { + restartVpnWithNewAppConfig( + networks, + reason = + "mtu? $isMtuChanged(o:${curnet?.minMtu}, n:${networks.minMtu}); routes? $isRoutesChanged" + ) + } } // Workaround for WireGuard connection issues after network change // WireGuard may fail to connect to the server when the network changes. @@ -1799,18 +1865,11 @@ class BraveVPNService : } } - private fun isRouteChanged(out: NetworkChanges, useActive: Boolean): Boolean { - // no need to check for routes if the app is not set to auto mode + private fun hasRouteChangedInAutoMode(out: NetworkChanges): Boolean { + // no need to check for routes if the app is not set in auto mode if (!appConfig.getInternetProtocol().isIPv46()) { return false } - - // if use active is set, then only consider active routes - if (useActive) { - return out.activeRoutesChanged - } - - // if use active is not set, then consider all the routes return out.routesChanged } @@ -1822,7 +1881,6 @@ class BraveVPNService : data class NetworkChanges( val routesChanged: Boolean = true, - val activeRoutesChanged: Boolean = true, val netChanged: Boolean = true, val mtuChanged: Boolean = true ) @@ -1835,36 +1893,38 @@ class BraveVPNService : if (old == null) return NetworkChanges() val mtuChanged = old.minMtu != new.minMtu - if (new.useActive) { - connectivityManager.activeNetwork?.let { new2 -> - // check if current active network, new2, also exists in old networks - // if so, routes have remained the same despite network changes - val had4 = old.ipv4Net.any { isNetworkSame(it.network, new2) } - val had6 = old.ipv6Net.any { isNetworkSame(it.network, new2) } - val routesChanged = !had4 || !had6 - // in case of use active, only consider first network as active - val hadActive4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, new2) - val hadActive6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, new2) - val activeRoutesChanged = !hadActive4 || !hadActive6 - // fixme: similar to actievRoutesChanged, should remove this? - // check if the first networks are different - val oldnet6 = old.ipv6Net.firstOrNull()?.network - val oldnet4 = old.ipv4Net.firstOrNull()?.network - val netChanged = !isNetworkSame(oldnet6, new2) || !isNetworkSame(oldnet4, new2) - - return NetworkChanges(routesChanged, activeRoutesChanged, netChanged, mtuChanged) + connectivityManager.activeNetwork?.let { activ -> + val oldHas4 = controllerProto.first // current tunnel routes ipv4? + val oldHas6 = controllerProto.second // current tunnel routes ipv6? + val activHas4 = isNetworkSame(new.ipv4Net.firstOrNull()?.network, activ) + val activHas6 = isNetworkSame(new.ipv6Net.firstOrNull()?.network, activ) + val both4 = + oldHas4 == activHas4 // old & new agree on activ capable of routing ipv4 or not + val both6 = + oldHas6 == activHas6 // old & new agree on activ capable of routing ipv6 or not + val routesChanged = !both4 || !both6 + + val oldnet4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, activ) + val oldnet6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, activ) + val bothnet4 = + oldnet4 == activHas4 // routing for ipv4 is same in old and new FIRST network + val bothnet6 = + oldnet6 == activHas6 // routing for ipv6 is same in old and new FIRST network + val netChanged = !bothnet4 || !bothnet6 + // for active networks, changes in routes includes all possible network changes; + return NetworkChanges(routesChanged, netChanged, mtuChanged) } // active network unknown, fallthrough } - // check if ipv6 or ipv4 routes changed + // check if ipv6 or ipv4 routes are different in old and new networks val old6 = old.ipv6Net.isNotEmpty() val new6 = new.ipv6Net.isNotEmpty() val old4 = old.ipv4Net.isNotEmpty() val new4 = new.ipv4Net.isNotEmpty() val routesChanged = old6 != new6 || old4 != new4 - // check if the first networks are different + // check if the first networks are different to urge rebinds where necessary (ex: WireGuard) val oldnet6 = old.ipv6Net.firstOrNull()?.network val newnet6 = new.ipv6Net.firstOrNull()?.network val oldnet4 = old.ipv4Net.firstOrNull()?.network @@ -1884,18 +1944,8 @@ class BraveVPNService : VpnController.isVpnLockdown() // active nw is null when lockdown ) { val dl: MutableList = mutableListOf() - currNet?.ipv4Net?.forEach { - val n = it.network - val lp = it.linkProperties - Log.i(LOG_TAG_VPN, "dns servers for network: $n, $lp") - lp?.dnsServers?.let { it1 -> dl.addAll(it1) } - } - currNet?.ipv6Net?.forEach { - val n = it.network - val lp = it.linkProperties - Log.i(LOG_TAG_VPN, "dns servers for network: $n, $lp") - lp?.dnsServers?.let { it1 -> dl.addAll(it1) } - } + dl.addAll(currNet?.dnsServers?.keys?.toList() ?: emptyList()) + Log.i(LOG_TAG_VPN, "dns servers ipv4,ipv6: $dl") dl } else { // get dns servers from the first network or active network @@ -1974,8 +2024,8 @@ class BraveVPNService : if (list.isNullOrEmpty()) { // regardless of rinr loopback mode, always use LOOPBACK_DNS to avoid leaks - Log.w(LOG_TAG_VPN, "No System DNS servers; unsetting existing $LOOPBACK_DNS") - dnsList.add(LOOPBACK_DNS) + Log.w(LOG_TAG_VPN, "No System DNS servers; unsetting existing $FALLBACK_DNS") + dnsList.add(FALLBACK_DNS) } else { dnsList.addAll(list) } @@ -1992,7 +2042,7 @@ class BraveVPNService : private fun handleVpnLockdownStateAsync() { if (!syncLockdownState()) return Log.i(LOG_TAG_VPN, "vpn lockdown mode change, restarting") - io("lockdownSync") { restartVpnWithNewAppConfig() } + io("lockdownSync") { restartVpnWithNewAppConfig(reason = "lockdownSync") } } private fun syncLockdownState(): Boolean { @@ -2107,7 +2157,7 @@ class BraveVPNService : } private fun handleVpnServiceOnAppStateChange() { // paused or resumed - io("pauseOrResumed") { restartVpnWithNewAppConfig() } + io("pauseOrResumed") { restartVpnWithNewAppConfig(reason = "pauseOrResumed") } ui { notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) } } @@ -2134,15 +2184,17 @@ class BraveVPNService : } } + // var to update the controller with the protocol set for the vpn private var controllerProto: Pair = Pair(false, false) - private suspend fun establishVpn(networks: Networks): ParcelFileDescriptor? { + private suspend fun establishVpn(networks: Networks): Pair { try { val mtu = mtu() // get mtu from the underlyingnetworks var builder: VpnService.Builder = newBuilder().setSession("Rethink").setMtu(mtu) var has6 = route6(networks) var has4 = route4(networks) + val nwProto = InternetProtocol.byProtos(has4, has6).value() controllerProto = Pair(has4, has6) Log.i(LOG_TAG_VPN, "Building vpn for v4? $has4, v6? $has6") @@ -2194,10 +2246,12 @@ class BraveVPNService : builder.allowFamily(AF_INET6) } } - return builder.establish() + val fd = builder.establish() + return Pair(fd, nwProto) } catch (e: Exception) { Log.e(LOG_TAG_VPN, e.message, e) - return null + // default to Settings.Ns46 + return Pair(null, InternetProtocol.IPv46.value()) } } @@ -2266,7 +2320,12 @@ class BraveVPNService : // considered // only for overlay network changes. Therefore, the VPN needs to be restarted // to recalculate the decision of adding routes. - io("overlayNwChanged") { restartVpnWithNewAppConfig(overlayNws = overlayNetworks) } + io("overlayNwChanged") { + restartVpnWithNewAppConfig( + overlayNws = overlayNetworks, + reason = "overlayNwChanged" + ) + } } else { Log.i(LOG_TAG_VPN, "overlay network not changed, no restart needed") } diff --git a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt index 621a16744..89252a463 100644 --- a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt +++ b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt @@ -259,7 +259,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : val useActive: Boolean, val minMtu: Int, var isActiveNetworkMetered: Boolean, // may be updated by client listener - var lastUpdated: Long // may be updated by client listener + var lastUpdated: Long, // may be updated by client listener + val dnsServers: LinkedHashMap ) // Handles the network messages from the callback from the connectivity manager @@ -376,6 +377,13 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : LOG_TAG_CONNECTION, "inform network change: ${sz}, all? $useActiveNetwork, metered? $isActiveNetworkMetered" ) + // maintain a map of dns servers for ipv4 and ipv6 networks + val dns4 = getDnsServers(trackedIpv4Networks) + val dns6 = getDnsServers(trackedIpv6Networks) + val dnsServers: LinkedHashMap = LinkedHashMap() + dnsServers.putAll(dns4) + dnsServers.putAll(dns6) + if (sz > 0) { val underlyingNetworks = UnderlyingNetworks( @@ -384,8 +392,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : useActiveNetwork, determineMtu(useActiveNetwork), isActiveNetworkMetered, - SystemClock.elapsedRealtime() - ) + SystemClock.elapsedRealtime(), + dnsServers) if (DEBUG) { trackedIpv4Networks.forEach { Log.d(LOG_TAG_CONNECTION, "inform4: ${it.network}, ${it.networkType}, $sz") @@ -403,12 +411,27 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : useActiveNetwork, DEFAULT_MTU, isActiveNetworkMetered = false, - SystemClock.elapsedRealtime() + SystemClock.elapsedRealtime(), + LinkedHashMap() ) listener.onNetworkDisconnected(underlyingNetworks) } } + private fun getDnsServers( + nws: LinkedHashSet + ): LinkedHashMap { + // add dns servers into a set to avoid duplicates and add corresponding network to it + val dnsServers = LinkedHashMap() + nws.forEach { + it.linkProperties?.dnsServers?.forEach v@{ dns -> + val address = dns ?: return@v + dnsServers[address] = it.network + } + } + return dnsServers + } + private fun determineMtu(useActiveNetwork: Boolean): Int { var minMtu4: Int = DEFAULT_MTU var minMtu6: Int = DEFAULT_MTU diff --git a/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt b/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt index 3dd21053c..e4b8a68b9 100644 --- a/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt +++ b/app/src/main/java/com/celzero/bravedns/service/IpRulesManager.kt @@ -480,7 +480,7 @@ object IpRulesManager : KoinComponent { // translated from go, net.SplitHostPort() class AddrError(val err: String, val addr: String) : Exception() - private fun splitHostPort(hostport: String): Triple { + fun splitHostPort(hostport: String): Triple { val missingPort = "missing port in address" val tooManyColons = "too many colons in address" diff --git a/app/src/main/java/com/celzero/bravedns/util/InternetProtocol.kt b/app/src/main/java/com/celzero/bravedns/util/InternetProtocol.kt index cfdc2ec11..b15238e7e 100644 --- a/app/src/main/java/com/celzero/bravedns/util/InternetProtocol.kt +++ b/app/src/main/java/com/celzero/bravedns/util/InternetProtocol.kt @@ -40,6 +40,15 @@ enum class InternetProtocol(val id: Int) { } } + fun byProtos(has4: Boolean, has6: Boolean): InternetProtocol { + return when { + has4 && has6 -> IPv46 + has4 -> IPv4 + has6 -> IPv6 + else -> IPv4 + } + } + fun isAuto(id: Int): Boolean { return id == IPv46.id } From 879dd87376a72c55edbdc2d1675a48555a377d08 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sat, 30 Mar 2024 15:33:24 +0530 Subject: [PATCH 61/81] bump firestack --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 70f6cfa1a..6481e915f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,8 +247,8 @@ dependencies { fullImplementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9' // from: https://jitpack.io/#celzero/firestack - download 'com.github.celzero:firestack:cb7d467a74@aar' - implementation 'com.github.celzero:firestack:cb7d467a74@aar' + download 'com.github.celzero:firestack:95d51bcbcc@aar' + implementation 'com.github.celzero:firestack:95d51bcbcc@aar' // Work manager implementation('androidx.work:work-runtime-ktx:2.9.0') { From 038cc92b63433f3a0d3469d18fc92a6495f43357 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sat, 30 Mar 2024 15:34:35 +0530 Subject: [PATCH 62/81] ui: show lockdown stroke when inactive --- .../java/com/celzero/bravedns/adapter/WgConfigAdapter.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt index 03007d0ea..a196c9aa4 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt @@ -19,7 +19,6 @@ import android.content.Context import android.content.Intent import android.os.SystemClock import android.text.format.DateUtils -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -243,8 +242,11 @@ class WgConfigAdapter(private val context: Context) : b.interfaceConfigStatus.text = context.getString(R.string.catch_all_wg_dialog_title) return // no need to update the apps count } else if (config.isLockdown) { - b.interfaceDetailCard.strokeWidth = 2 - b.interfaceDetailCard.strokeColor = UIUtils.fetchColor(context, R.attr.accentBad) + if (!config.isActive) { + b.interfaceDetailCard.strokeWidth = 2 + b.interfaceDetailCard.strokeColor = + UIUtils.fetchColor(context, R.attr.accentBad) + } b.interfaceConfigStatus.visibility = View.VISIBLE b.interfaceConfigStatus.text = context.getString(R.string.firewall_rule_global_lockdown) From f1bc294a1e1017f1a12c528bff8efe5e710c0c3a Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sun, 31 Mar 2024 05:50:49 +0530 Subject: [PATCH 63/81] pptimize netlog: combine add and update operations into single-threaded batch --- .../celzero/bravedns/service/NetLogTracker.kt | 79 +++++++------------ .../celzero/bravedns/util/NetLogBatcher.kt | 74 ++++++++++++----- 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt index ac8a35b4c..197892061 100644 --- a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt @@ -28,20 +28,20 @@ import com.celzero.bravedns.database.DnsLogRepository import com.celzero.bravedns.database.RethinkLog import com.celzero.bravedns.database.RethinkLogRepository import com.celzero.bravedns.util.NetLogBatcher +import java.util.Calendar import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import java.util.Calendar class NetLogTracker internal constructor( - private val context: Context, - private val connectionTrackerRepository: ConnectionTrackerRepository, - private val rethinkLogRepository: RethinkLogRepository, - private val dnsLogRepository: DnsLogRepository, + context: Context, + connectionTrackerRepository: ConnectionTrackerRepository, + rethinkLogRepository: RethinkLogRepository, + dnsLogRepository: DnsLogRepository, private val persistentState: PersistentState ) : KoinComponent { @@ -49,49 +49,32 @@ internal constructor( private var scope: CoroutineScope? = null - private var dnsLogTracker: DnsLogTracker? = null - private var ipTracker: IPTracker? = null + private var dnsLogTracker: DnsLogTracker = + DnsLogTracker(dnsLogRepository, persistentState, context) + private var ipTracker: IPTracker = + IPTracker(connectionTrackerRepository, rethinkLogRepository, context) - private var dnsNetLogBatcher: NetLogBatcher? = null - private var ipNetLogBatcher: NetLogBatcher? = null - private var summaryBatcher: NetLogBatcher? = null - private var rethinkLogBatcher: NetLogBatcher? = null - private var rethinkSummaryBatcher: NetLogBatcher? = null + private var dnsNetLogBatcher: NetLogBatcher = + NetLogBatcher("dns", dnsLogTracker::insertBatch) + private var ipNetLogBatcher: NetLogBatcher = + NetLogBatcher("ip", ipTracker::insertBatch, ipTracker::updateBatch) + private var rethinkLogBatcher: NetLogBatcher = + NetLogBatcher("rinr", ipTracker::insertRethinkBatch, ipTracker::updateRethinkBatch) suspend fun startLogger(s: CoroutineScope) { - if (ipTracker == null) { - ipTracker = IPTracker(connectionTrackerRepository, rethinkLogRepository, context) - } - - if (dnsLogTracker == null) { - dnsLogTracker = DnsLogTracker(dnsLogRepository, persistentState, context) - } - this.scope = s - // asserting, created object above - ipNetLogBatcher = NetLogBatcher(ipTracker!!::insertBatch) - ipNetLogBatcher!!.begin(scope!!) - - dnsNetLogBatcher = NetLogBatcher(dnsLogTracker!!::insertBatch) - dnsNetLogBatcher!!.begin(scope!!) - - summaryBatcher = NetLogBatcher(ipTracker!!::updateBatch) - summaryBatcher!!.begin(scope!!) - - rethinkLogBatcher = NetLogBatcher(ipTracker!!::insertRethinkBatch) - rethinkLogBatcher!!.begin(scope!!) - - rethinkSummaryBatcher = NetLogBatcher(ipTracker!!::updateRethinkBatch) - rethinkSummaryBatcher!!.begin(scope!!) + dnsNetLogBatcher.begin(s) + ipNetLogBatcher.begin(s) + rethinkLogBatcher.begin(s) } fun writeIpLog(info: ConnTrackerMetaData) { if (!persistentState.logsEnabled) return io("writeIpLog") { - val connTracker = ipTracker?.makeConnectionTracker(info) ?: return@io - ipNetLogBatcher?.add(connTracker) + val connTracker = ipTracker.makeConnectionTracker(info) + ipNetLogBatcher.add(connTracker) } } @@ -99,25 +82,23 @@ internal constructor( if (!persistentState.logsEnabled) return io("writeRethinkLog") { - val rlog = ipTracker?.makeRethinkLogs(info) ?: return@io - rethinkLogBatcher?.add(rlog) + val rlog = ipTracker.makeRethinkLogs(info) ?: return@io + rethinkLogBatcher.add(rlog) } } fun updateIpSummary(summary: ConnectionSummary) { if (!persistentState.logsEnabled) return - if (ipTracker == null) return - io("writeIpSummary") { val s = if (DEBUG && summary.targetIp?.isNotEmpty() == true) { - ipTracker!!.makeSummaryWithTarget(summary) + ipTracker.makeSummaryWithTarget(summary) } else { summary } - summaryBatcher?.add(s) + ipNetLogBatcher.update(s) } } @@ -127,31 +108,31 @@ internal constructor( io("writeRethinkSummary") { val s = if (DEBUG && summary.targetIp?.isNotEmpty() == true) { - ipTracker!!.makeSummaryWithTarget(summary) + ipTracker.makeSummaryWithTarget(summary) } else { summary } - rethinkSummaryBatcher?.add(s) + rethinkLogBatcher.update(s) } } // now, this method is doing multiple things which should be removed. // fixme: should intend to only write the logs to database. fun processDnsLog(summary: DNSSummary) { - val transaction = dnsLogTracker?.processOnResponse(summary) ?: return + val transaction = dnsLogTracker.processOnResponse(summary) transaction.responseCalendar = Calendar.getInstance() // refresh latency from GoVpnAdapter io("refreshDnsLatency") { dnsLatencyTracker.refreshLatencyIfNeeded(transaction) } // TODO: This method should be part of BraveVPNService - dnsLogTracker?.updateVpnConnectionState(transaction) + dnsLogTracker.updateVpnConnectionState(transaction) if (!persistentState.logsEnabled) return - val dnsLog = dnsLogTracker?.makeDnsLogObj(transaction) ?: return - io("dnsLogger") { dnsNetLogBatcher?.add(dnsLog) } + val dnsLog = dnsLogTracker.makeDnsLogObj(transaction) + io("dnsLogger") { dnsNetLogBatcher.add(dnsLog) } } private fun io(s: String, f: suspend () -> Unit) = diff --git a/app/src/main/java/com/celzero/bravedns/util/NetLogBatcher.kt b/app/src/main/java/com/celzero/bravedns/util/NetLogBatcher.kt index d6ffcff42..9ae789bc0 100644 --- a/app/src/main/java/com/celzero/bravedns/util/NetLogBatcher.kt +++ b/app/src/main/java/com/celzero/bravedns/util/NetLogBatcher.kt @@ -18,10 +18,12 @@ package com.celzero.bravedns.util import android.util.Log import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG +import com.celzero.bravedns.util.Logger.Companion.LOG_BATCH_LOGGER import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.async import kotlinx.coroutines.awaitCancellation @@ -34,17 +36,22 @@ import kotlinx.coroutines.withContext // channel buffer receives batched entries of batchsize or once every waitms from a batching // producer or a time-based monitor (signal) running in a single-threaded co-routine context. -class NetLogBatcher(val processor: suspend (List) -> Unit) { +class NetLogBatcher( + val tag: String, + val processor: suspend (List) -> Unit, + val updator: suspend (List) -> Unit = { _ -> } +) { // i keeps track of currently in-use buffer var lsn = 0 // a single thread to run sig and batch co-routines in; // to avoid use of mutex/semaphores over shared-state - @OptIn(DelicateCoroutinesApi::class) val looper = newSingleThreadContext("logLooper") + @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) + val looper = newSingleThreadContext(tag + "Looper") - private val nprod = CoroutineName("logProducer") // batches writes - private val nsig = CoroutineName("logSignal") - private val ncons = CoroutineName("logConsumer") // writes batches to db + private val nprod = CoroutineName(tag + "Producer") // batches writes + private val nsig = CoroutineName(tag + "Signal") + private val ncons = CoroutineName(tag + "Consumer") // writes batches to db // dispatch buffer to consumer if greater than batch size private val batchSize = 20 @@ -57,17 +64,20 @@ class NetLogBatcher(val processor: suspend (List) -> Unit) { private val waitms = 2500L // buffer channel, holds at most 2 buffers, and drops the oldest - private val buffers = Channel>(qsize, BufferOverflow.DROP_OLDEST) + private val buffersCh = Channel>(qsize, BufferOverflow.DROP_OLDEST) + private val updatesCh = Channel>(qsize, BufferOverflow.DROP_OLDEST) // signal channel, holds at most 1 signal, and drops the oldest private val signal = Channel(Channel.Factory.CONFLATED) private var batches = mutableListOf() + private var updates = mutableListOf() fun begin(scope: CoroutineScope) { // launch suspend fns sig and consume asynchronously scope.async { sig() } - scope.async { consume() } + scope.async { consumeAdd() } + scope.async { consumeUpdate() } // monitor for cancellation on the default dispatcher scope.launch { monitorCancellation() } } @@ -80,24 +90,38 @@ class NetLogBatcher(val processor: suspend (List) -> Unit) { withContext(NonCancellable) { looper.close() signal.close() - buffers.close() + buffersCh.close() + updatesCh.close() } } } - private suspend fun consume() = + private suspend fun consumeAdd() = withContext(Dispatchers.IO + ncons) { - for (y in buffers) { + for (y in buffersCh) { processor(y) } } + private suspend fun consumeUpdate() = + withContext(Dispatchers.IO + ncons) { + for (y in updatesCh) { + updator(y) + } + } + private suspend fun txswap() { val b = batches batches = mutableListOf() // swap buffers - if (DEBUG) Log.d(Logger.LOG_BATCH_LOGGER, "transfer and swap (${lsn}) ${b.size}") + buffersCh.send(b) + + val u = updates + updates = mutableListOf() + updatesCh.send(u) + + if (DEBUG) Log.d(LOG_BATCH_LOGGER, "transfer and swap (${lsn}) u: ${u.size}, b: ${b.size}") + lsn = (lsn + 1) - buffers.send(b) } suspend fun add(payload: T) = @@ -111,32 +135,46 @@ class NetLogBatcher(val processor: suspend (List) -> Unit) { } } + suspend fun update(payload: V) = + withContext(looper + nprod) { + updates.add(payload) + if (updates.size >= batchSize) { + txswap() + } else if (updates.size == 1) { + signal.send(lsn) + } + } + private suspend fun sig() = withContext(looper + nsig) { // consume all signals for (tracklsn in signal) { + if (tracklsn < lsn) { + if (DEBUG) Log.d(LOG_BATCH_LOGGER, "dup signal skip $tracklsn") + continue + } // do not honor the signal for 'l' if a[l] is empty // this can happen if the signal for 'l' is processed // after the fact that 'l' has been swapped out by 'batch' - if (batches.size <= 0) { - if (DEBUG) Log.d(Logger.LOG_BATCH_LOGGER, "signal continue") + if (batches.size <= 0 && updates.size <= 0) { + if (DEBUG) Log.d(LOG_BATCH_LOGGER, "signal continue") continue } else { - if (DEBUG) Log.d(Logger.LOG_BATCH_LOGGER, "signal sleep $waitms ms") + if (DEBUG) Log.d(LOG_BATCH_LOGGER, "signal sleep $waitms ms") } // wait for 'batch' to dispatch delay(waitms) if (DEBUG) Log.d( - Logger.LOG_BATCH_LOGGER, - "signal wait over, sz(${batches.size}) / cur-buf(${lsn})" + LOG_BATCH_LOGGER, + "signal wait over, sz(b: ${batches.size}, u: ${updates.size}) / cur-buf(${lsn})" ) // 'l' is the current buffer, that is, 'l == i', // and 'batch' hasn't dispatched it, // but time's up... - if (lsn == tracklsn && batches.size > 0) { + if (lsn == tracklsn && (batches.size > 0 || updates.size > 0)) { txswap() } } From 604f2caae8a6a849ccb3bdf0ef24531f79e36b12 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Sun, 31 Mar 2024 05:52:59 +0530 Subject: [PATCH 64/81] utilize unregisterReceiver method with error handling instead of context.unregisterReceiver --- app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt index cba0bf161..51d6ed17e 100644 --- a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt +++ b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt @@ -231,7 +231,7 @@ class OrbotHelper( STATUS_OFF -> { Log.i(LOG_TAG_VPN, "Orbot is OFF, retry or stop the Orbot") io { waitForOrbot() } - context.unregisterReceiver(this) + unregisterReceiver() } STATUS_STARTING -> { Log.i(LOG_TAG_VPN, "Orbot is STARTING, update the proxy data") @@ -472,7 +472,7 @@ class OrbotHelper( } else { uiCtx { stopOrbot(isInteractive = false) - context.unregisterReceiver(orbotStatusReceiver) + unregisterReceiver() } } } @@ -482,7 +482,7 @@ class OrbotHelper( try { context.unregisterReceiver(orbotStatusReceiver) } catch (e: IllegalArgumentException) { - Log.w(LOG_TAG_VPN, "Unregister not needed: ${e.message}") + Log.w(LOG_TAG_VPN, "orbot unregister not needed: ${e.message}") } } From 133b335c233c6b3b9ec09ba20bf366a0bf9e9be7 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 18:19:28 +0530 Subject: [PATCH 65/81] upgrade AGP version from 8.2.2 to 8.3.1 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 23757d223..c7ddfcc2c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' + classpath 'com.android.tools.build:gradle:8.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 700a44cde..1dd036412 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Apr 17 17:05:36 IST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From e09dd900a510b55795016698fac2dd76cbd2bfa4 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 19:59:51 +0530 Subject: [PATCH 66/81] move logs to separate screen from app info activity --- .../bravedns/adapter/AppConnectionAdapter.kt | 48 ++--- .../bravedns/ui/activity/AppInfoActivity.kt | 199 +----------------- .../ui/activity/AppWiseLogsActivity.kt | 126 ++++++++++- .../viewmodel/AppConnectionsViewModel.kt | 41 +--- .../full/res/layout/activity_app_details.xml | 195 +++-------------- .../res/layout/list_item_app_conn_details.xml | 2 + .../bravedns/database/ConnectionTrackerDAO.kt | 10 - 7 files changed, 185 insertions(+), 436 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt index 9604c2273..8f1d453cd 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt @@ -146,30 +146,26 @@ class AppConnectionAdapter(val context: Context, val lifecycleOwner: LifecycleOw } private fun updateStatusUi(uid: Int, ipAddress: String) { - io { - val status = IpRulesManager.getMostSpecificRuleMatch(uid, ipAddress) - uiCtx { - when (status) { - IpRulesManager.IpRuleStatus.NONE -> { - b.acdFlag.text = context.getString(R.string.ci_no_rule_initial) - } - IpRulesManager.IpRuleStatus.BLOCK -> { - b.acdFlag.text = context.getString(R.string.ci_blocked_initial) - } - IpRulesManager.IpRuleStatus.BYPASS_UNIVERSAL -> { - b.acdFlag.text = context.getString(R.string.ci_bypass_universal_initial) - } - IpRulesManager.IpRuleStatus.TRUST -> { - b.acdFlag.text = context.getString(R.string.ci_trust_initial) - } - } - - // returns the text and background color for the button - val t = getToggleBtnUiParams(status) - b.acdFlag.setTextColor(t.txtColor) - b.acdFlag.backgroundTintList = ColorStateList.valueOf(t.bgColor) + val status = IpRulesManager.getMostSpecificRuleMatch(uid, ipAddress) + when (status) { + IpRulesManager.IpRuleStatus.NONE -> { + b.acdFlag.text = context.getString(R.string.ci_no_rule_initial) + } + IpRulesManager.IpRuleStatus.BLOCK -> { + b.acdFlag.text = context.getString(R.string.ci_blocked_initial) + } + IpRulesManager.IpRuleStatus.BYPASS_UNIVERSAL -> { + b.acdFlag.text = context.getString(R.string.ci_bypass_universal_initial) + } + IpRulesManager.IpRuleStatus.TRUST -> { + b.acdFlag.text = context.getString(R.string.ci_trust_initial) } } + + // returns the text and background color for the button + val t = getToggleBtnUiParams(status) + b.acdFlag.setTextColor(t.txtColor) + b.acdFlag.backgroundTintList = ColorStateList.valueOf(t.bgColor) } private fun getToggleBtnUiParams(id: IpRulesManager.IpRuleStatus): ToggleBtnUi { @@ -205,12 +201,4 @@ class AppConnectionAdapter(val context: Context, val lifecycleOwner: LifecycleOw override fun notifyDataset(position: Int) { this.notifyItemChanged(position) } - - private suspend fun uiCtx(f: suspend () -> Unit) { - withContext(Dispatchers.Main) { f() } - } - - private fun io(f: suspend () -> Unit) { - lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { f() } - } } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt index 2b22768c0..1bc75280f 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt @@ -23,26 +23,19 @@ import android.graphics.drawable.Drawable import android.os.Bundle import android.view.View import android.widget.ArrayAdapter -import android.widget.CompoundButton import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.TooltipCompat import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.LinearLayoutManager import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide import com.celzero.bravedns.R -import com.celzero.bravedns.adapter.AppConnectionAdapter -import com.celzero.bravedns.data.AppConfig import com.celzero.bravedns.database.AppInfo import com.celzero.bravedns.database.ConnectionTrackerRepository -import com.celzero.bravedns.database.RethinkDnsEndpoint import com.celzero.bravedns.databinding.ActivityAppDetailsBinding import com.celzero.bravedns.service.FirewallManager import com.celzero.bravedns.service.FirewallManager.updateFirewallStatus @@ -69,12 +62,10 @@ import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -class AppInfoActivity : - AppCompatActivity(R.layout.activity_app_details), SearchView.OnQueryTextListener { +class AppInfoActivity : AppCompatActivity(R.layout.activity_app_details) { private val b by viewBinding(ActivityAppDetailsBinding::bind) private val persistentState by inject() - private val appConfig by inject() private val connectionTrackerRepository by inject() private val ipRulesViewModel: CustomIpViewModel by viewModel() @@ -84,8 +75,8 @@ class AppInfoActivity : private var uid: Int = INVALID_UID private lateinit var appInfo: AppInfo - private var ipListUiState: Boolean = true - private var firewallUiState: Boolean = false + private var ipListUiState: Boolean = false + private var firewallUiState: Boolean = true private var appStatus = FirewallManager.FirewallStatus.NONE private var connStatus = FirewallManager.ConnectionStatus.ALLOW @@ -122,33 +113,12 @@ class AppInfoActivity : networkLogsViewModel.getConnectionsCount(uid).observe(this) { if (it == null) return@observe - b.aadConnDetailDesc.text = getString(R.string.ada_ip_connection_count, it.toString()) - if (it == 0) { - b.aadConnDetailDesc.text = getString(R.string.ada_ip_connection_count_zero) - b.aadConnDetailRecycler.visibility = View.GONE - b.aadConnDetailEmptyTxt.visibility = View.VISIBLE - b.aadConnDetailSearchLl.visibility = View.GONE - - // toggle the state only when the firewall rules are not visible - if (firewallUiState) { - toggleFirewallUiState(firewallUiState) - toggleNetworkLogState(ipListUiState) - } - } else { - b.aadConnDetailRecycler.visibility = View.VISIBLE - b.aadConnDetailEmptyTxt.visibility = View.GONE - b.aadConnDetailSearchLl.visibility = View.VISIBLE - // if more than 100 logs, option to view all logs in a new screen - if (it > LOG_THRESHOLD_SIZE) { - b.aadConnShowAllChip.visibility = View.VISIBLE - } else { - b.aadConnShowAllChip.visibility = View.GONE - } - } + b.aadLogsDetail.text = it.toString() } } private fun init() { + b.aadLogsDetailDesc.text = getString(R.string.lbl_logs).replaceFirstChar { it.uppercase() } io { val appInfo = FirewallManager.getAppInfoByUid(uid) // case: app is uninstalled but still available in RethinkDNS database @@ -169,7 +139,6 @@ class AppInfoActivity : Utilities.getIcon(this, appInfo.packageName, appInfo.appName), b.aadAppDetailIcon ) - showNetworkLogsIfAny(appInfo.uid) // do not show the firewall status if the app is Rethink if (appInfo.packageName == this.packageName) { @@ -179,12 +148,7 @@ class AppInfoActivity : hideIpBlockUi() return@uiCtx } - - // introduce this on v056 - // updateDnsDetails() updateFirewallStatusUi(appStatus, connStatus) - toggleFirewallUiState(firewallUiState) - toggleNetworkLogState(ipListUiState) } } } @@ -201,7 +165,6 @@ class AppInfoActivity : b.aadIpBlockCard.visibility = View.GONE } - private fun openCustomIpScreen() { val intent = Intent(this, CustomRulesActivity::class.java) // this activity is either being started in a new task or bringing to the top an @@ -221,21 +184,6 @@ class AppInfoActivity : startActivity(intent) } - private fun updateDnsDetails() { - io { - val isDnsEnabled = appConfig.isAppWiseDnsEnabled(uid) - - uiCtx { - if (isDnsEnabled) { - enableDnsStatusUi() - return@uiCtx - } - - disableDnsStatusUi() - } - } - } - private fun updateDataUsage() { val u = Utilities.humanReadableByteCount(appInfo.uploadBytes, true) val uploadBytes = getString(R.string.symbol_upload, u) @@ -288,8 +236,6 @@ class AppInfoActivity : private fun setupClickListeners() { - b.aadConnDetailSearch.setOnQueryTextListener(this) - b.aadAppInfoIcon.setOnClickListener { io { val packages = FirewallManager.getAppNamesByUid(appInfo.uid) @@ -398,38 +344,11 @@ class AppInfoActivity : } } - b.aadConnDetailIndicator.setOnClickListener { toggleNetworkLogState(ipListUiState) } - - b.aadConnDetailRl.setOnClickListener { toggleNetworkLogState(ipListUiState) } - - b.aadAapFirewallIndicator.setOnClickListener { toggleFirewallUiState(firewallUiState) } - - b.aadAapFirewallNewCard.setOnClickListener { toggleFirewallUiState(firewallUiState) } - b.aadIpBlockCard.setOnClickListener { openCustomIpScreen() } b.aadDomainBlockCard.setOnClickListener { openCustomDomainScreen() } - b.aadAppDetailIcon.setOnClickListener { toggleFirewallUiState(firewallUiState) } - - b.aadAppDetailLl.setOnClickListener { toggleFirewallUiState(firewallUiState) } - - b.aadDnsHeading.setOnCheckedChangeListener { _: CompoundButton, isSelected: Boolean -> - if (isSelected) { - enableDnsStatusUi() - // fixme: remove the below code, added for testing - setAppDns("https://basic.rethinkdns.com/1:IAAQAA==") - return@setOnCheckedChangeListener - } - - removeAppDns(uid) - } - - b.aadAppDnsRethinkConfigure.setOnClickListener { rethinkListBottomSheet() } - - b.aadConnDelete.setOnClickListener { showDeleteConnectionsDialog() } - - b.aadConnShowAllChip.setOnClickListener { + b.aadLogsCard.setOnClickListener { val intent = Intent(this, AppWiseLogsActivity::class.java) intent.putExtra(UID_INTENT_NAME, uid) startActivity(intent) @@ -458,45 +377,11 @@ class AppInfoActivity : alertDialog.show() } - private fun setAppDns(url: String) { - io { - val endpoint = - RethinkDnsEndpoint( - "app_${appInfo.appName}", - url, - uid, - desc = "", - isActive = false, - isCustom = true, - latency = 0, - blocklistCount = 0, - modifiedDataTime = Constants.INIT_TIME_MS - ) - appConfig.insertReplaceEndpoint(endpoint) - } - } - private fun rethinkListBottomSheet() { val bottomSheetFragment = RethinkListBottomSheet() bottomSheetFragment.show(this.supportFragmentManager, bottomSheetFragment.tag) } - private fun removeAppDns(uid: Int) { - io { appConfig.removeAppWiseDns(uid) } - - disableDnsStatusUi() - } - - private fun disableDnsStatusUi() { - b.aadDnsRethinkRl.visibility = View.GONE - b.aadDnsHeading.isChecked = false - } - - private fun enableDnsStatusUi() { - b.aadDnsRethinkRl.visibility = View.VISIBLE - b.aadDnsHeading.isChecked = true - } - private fun toggleMobileData(appInfo: AppInfo) { // toggle mobile data: change the connection status based on the current status. // if allow -> none(app status) + metered(connection status) @@ -585,50 +470,6 @@ class AppInfoActivity : updateFirewallStatusUi(aStat, cStat) } - private fun showNetworkLogsIfAny(uid: Int) { - networkLogsViewModel.setUid(uid) - b.aadConnDetailRecycler.setHasFixedSize(true) - val layoutManager = LinearLayoutManager(this) - b.aadConnDetailRecycler.layoutManager = layoutManager - val recyclerAdapter = AppConnectionAdapter(this, this, uid) - networkLogsViewModel.appNetworkLogs.observe(this) { - recyclerAdapter.submitData(this.lifecycle, it) - } - b.aadConnDetailRecycler.isNestedScrollingEnabled = true - b.aadConnDetailRecycler.adapter = recyclerAdapter - val itemAnimator = DefaultItemAnimator() - itemAnimator.changeDuration = 1000 - b.aadConnDetailRecycler.itemAnimator = itemAnimator - } - - private fun toggleNetworkLogState(state: Boolean) { - ipListUiState = !state - - if (state) { - b.aadConnListTopLl.visibility = View.VISIBLE - b.aadConnDetailIndicator.setImageResource(R.drawable.ic_arrow_up) - b.aadConnDelete.visibility = View.VISIBLE - b.aadConnDetailSearchLl.visibility = View.VISIBLE - } else { - b.aadConnListTopLl.visibility = View.GONE - b.aadConnDetailIndicator.setImageResource(R.drawable.ic_arrow_down) - b.aadConnDelete.visibility = View.GONE - b.aadConnDetailSearchLl.visibility = View.GONE - } - } - - private fun toggleFirewallUiState(state: Boolean) { - firewallUiState = !state - - if (state) { - b.aadAppSettingsLl.visibility = View.VISIBLE - b.aadAapFirewallIndicator.setImageResource(R.drawable.ic_arrow_up) - } else { - b.aadAppSettingsLl.visibility = View.GONE - b.aadAapFirewallIndicator.setImageResource(R.drawable.ic_arrow_down) - } - } - private fun enableAppBypassedUi() { setDrawable(R.drawable.ic_bypass_dns_firewall_off, b.aadAppSettingsBypassDnsFirewall) setDrawable(R.drawable.ic_firewall_wifi_on_grey, b.aadAppSettingsBlockWifi) @@ -770,21 +611,6 @@ class AppInfoActivity : builder.create().show() } - private fun showDeleteConnectionsDialog() { - val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(R.string.ada_delete_logs_dialog_title) - builder.setMessage(R.string.ada_delete_logs_dialog_desc) - builder.setCancelable(true) - builder.setPositiveButton(getString(R.string.lbl_proceed)) { _, _ -> deleteAppLogs() } - - builder.setNegativeButton(getString(R.string.lbl_cancel)) { _, _ -> } - builder.create().show() - } - - private fun deleteAppLogs() { - io { connectionTrackerRepository.clearLogsByUid(uid) } - } - private fun showDialog( packageList: List, appInfo: AppInfo, @@ -840,17 +666,4 @@ class AppInfoActivity : withContext(Dispatchers.Main) { f() } } - override fun onQueryTextSubmit(query: String): Boolean { - networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.OFFSET) - return true - } - - override fun onQueryTextChange(query: String): Boolean { - Utilities.delay(500, lifecycleScope) { - if (!this.isFinishing) { - networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.OFFSET) - } - } - return true - } } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt index ad5939145..131969bdb 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/AppWiseLogsActivity.kt @@ -17,21 +17,32 @@ package com.celzero.bravedns.ui.activity import android.content.Context import android.content.res.Configuration +import android.graphics.drawable.Drawable import android.os.Bundle +import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import by.kirich1409.viewbindingdelegate.viewBinding +import com.bumptech.glide.Glide import com.celzero.bravedns.R import com.celzero.bravedns.adapter.AppConnectionAdapter +import com.celzero.bravedns.database.AppInfo +import com.celzero.bravedns.database.ConnectionTrackerRepository import com.celzero.bravedns.databinding.ActivityAppWiseLogsBinding +import com.celzero.bravedns.service.FirewallManager import com.celzero.bravedns.service.PersistentState -import com.celzero.bravedns.util.Constants +import com.celzero.bravedns.util.Constants.Companion.INVALID_UID import com.celzero.bravedns.util.Themes import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.viewmodel.AppConnectionsViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel @@ -41,8 +52,10 @@ class AppWiseLogsActivity : private val persistentState by inject() private val networkLogsViewModel: AppConnectionsViewModel by viewModel() - private var uid: Int = Constants.INVALID_UID + private val connectionTrackerRepository by inject() + private var uid: Int = INVALID_UID private var layoutManager: RecyclerView.LayoutManager? = null + private lateinit var appInfo: AppInfo private fun Context.isDarkThemeOn(): Boolean { return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == @@ -52,12 +65,57 @@ class AppWiseLogsActivity : override fun onCreate(savedInstanceState: Bundle?) { setTheme(Themes.getCurrentTheme(isDarkThemeOn(), persistentState.theme)) super.onCreate(savedInstanceState) - uid = intent.getIntExtra(AppInfoActivity.UID_INTENT_NAME, Constants.INVALID_UID) - if (uid == Constants.INVALID_UID) { + uid = intent.getIntExtra(AppInfoActivity.UID_INTENT_NAME, INVALID_UID) + if (uid == INVALID_UID) { finish() } - b.awlSearch.setOnQueryTextListener(this) + init() setAdapter() + observeNetworkLogSize() + setClickListener() + } + + private fun init() { + io { + val appInfo = FirewallManager.getAppInfoByUid(uid) + // case: app is uninstalled but still available in RethinkDNS database + if (appInfo == null || uid == INVALID_UID) { + uiCtx { finish() } + return@io + } + + val packages = FirewallManager.getPackageNamesByUid(appInfo.uid) + uiCtx { + this.appInfo = appInfo + + b.awlAppDetailName.text = appName(packages.count()) + displayIcon( + Utilities.getIcon(this, appInfo.packageName, appInfo.appName), + b.awlAppDetailIcon + ) + } + } + } + + private fun setClickListener() { + b.awlDelete.setOnClickListener { showDeleteConnectionsDialog() } + b.awlSearch.setOnQueryTextListener(this) + } + + private fun appName(packageCount: Int): String { + return if (packageCount >= 2) { + getString( + R.string.ctbs_app_other_apps, + appInfo.appName, + packageCount.minus(1).toString() + ) + } else { + appInfo.appName + } + } + + private fun displayIcon(drawable: Drawable?, mIconImageView: ImageView) { + Glide.with(this).load(drawable).error(Utilities.getDefaultIcon(this)).into(mIconImageView) } private fun setAdapter() { @@ -72,6 +130,41 @@ class AppWiseLogsActivity : b.awlRecyclerConnection.adapter = recyclerAdapter } + private fun observeNetworkLogSize() { + networkLogsViewModel.getConnectionsCount(uid).observe(this) { + if (it == null) return@observe + + if (it <= 0) { + showNoRulesUi() + hideRulesUi() + return@observe + } + + hideNoRulesUi() + showRulesUi() + } + } + + private fun showNoRulesUi() { + b.awlNoRulesRl.visibility = android.view.View.VISIBLE + } + + private fun hideRulesUi() { + b.awlCardViewTop.visibility = android.view.View.GONE + b.awlAppDetailRl.visibility = android.view.View.GONE + b.awlRecyclerConnection.visibility = android.view.View.GONE + } + + private fun hideNoRulesUi() { + b.awlNoRulesRl.visibility = android.view.View.GONE + } + + private fun showRulesUi() { + b.awlCardViewTop.visibility = android.view.View.VISIBLE + b.awlAppDetailRl.visibility = android.view.View.VISIBLE + b.awlRecyclerConnection.visibility = android.view.View.VISIBLE + } + override fun onQueryTextSubmit(query: String): Boolean { networkLogsViewModel.setFilter(query, AppConnectionsViewModel.FilterType.ALL) return true @@ -85,4 +178,27 @@ class AppWiseLogsActivity : } return true } + + private fun showDeleteConnectionsDialog() { + val builder = MaterialAlertDialogBuilder(this) + builder.setTitle(R.string.ada_delete_logs_dialog_title) + builder.setMessage(R.string.ada_delete_logs_dialog_desc) + builder.setCancelable(true) + builder.setPositiveButton(getString(R.string.lbl_proceed)) { _, _ -> deleteAppLogs() } + + builder.setNegativeButton(getString(R.string.lbl_cancel)) { _, _ -> } + builder.create().show() + } + + private fun deleteAppLogs() { + io { connectionTrackerRepository.clearLogsByUid(uid) } + } + + private fun io(f: suspend () -> Unit): Job { + return lifecycleScope.launch(Dispatchers.IO) { f() } + } + + private suspend fun uiCtx(f: suspend () -> Unit) { + withContext(Dispatchers.Main) { f() } + } } diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt index 014c3b94c..337eb7000 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/AppConnectionsViewModel.kt @@ -3,7 +3,6 @@ package com.celzero.bravedns.viewmodel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.asLiveData import androidx.lifecycle.switchMap import androidx.lifecycle.viewModelScope import androidx.paging.Pager @@ -27,10 +26,12 @@ class AppConnectionsViewModel(private val nwlogDao: ConnectionTrackerDAO) : View pagingConfig = PagingConfig( - enablePlaceholders = false, - prefetchDistance = 10, - initialLoadSize = Constants.LIVEDATA_PAGE_SIZE, - pageSize = Constants.LIVEDATA_PAGE_SIZE + enablePlaceholders = true, + prefetchDistance = 3, + initialLoadSize = Constants.LIVEDATA_PAGE_SIZE * 2, + maxSize = Constants.LIVEDATA_PAGE_SIZE * 3, + pageSize = Constants.LIVEDATA_PAGE_SIZE * 2, + jumpThreshold = 5 ) } @@ -39,38 +40,8 @@ class AppConnectionsViewModel(private val nwlogDao: ConnectionTrackerDAO) : View ALL } - val appNetworkLogs = filter.switchMap { input -> fetchNetworkLogs(uid, input) } - val allAppNetworkLogs = allLogsFilter.switchMap { input -> fetchAllNetworkLogs(uid, input) } - private fun fetchNetworkLogs(uid: Int, input: String): LiveData> { - val pager = - if (input.isEmpty()) { - Pager(config = pagingConfig, pagingSourceFactory = { nwlogDao.getLogsForAppWithLimit(uid) }) - .flow - .cachedIn(viewModelScope) - } else { - Pager( - config = pagingConfig, - pagingSourceFactory = { nwlogDao.getLogsForAppFilteredWithLimit(uid, "%$input%") } - ) - .flow - .cachedIn(viewModelScope) - } - - return pager.asLiveData() - - /*return if (input.isEmpty()) { - Pager(pagingConfig) { nwlogDao.getLogsForAppWithLimit(uid) } - .liveData - .cachedIn(viewModelScope) - } else { - Pager(pagingConfig) { nwlogDao.getLogsForAppFilteredWithLimit(uid, "%$input%") } - .liveData - .cachedIn(viewModelScope) - }*/ - } - private fun fetchAllNetworkLogs(uid: Int, input: String): LiveData> { return if (input.isEmpty()) { Pager(pagingConfig) { nwlogDao.getAllLogs(uid) }.liveData.cachedIn(viewModelScope) diff --git a/app/src/full/res/layout/activity_app_details.xml b/app/src/full/res/layout/activity_app_details.xml index eb5e050a2..fc3a05da2 100644 --- a/app/src/full/res/layout/activity_app_details.xml +++ b/app/src/full/res/layout/activity_app_details.xml @@ -1,5 +1,5 @@ - + android:padding="10dp"> - + android:paddingTop="10dp" + android:paddingBottom="10dp"> + android:paddingTop="10dp" + android:paddingBottom="10dp"> - - + android:paddingTop="10dp" + android:paddingBottom="10dp"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:fontFamily="sans-serif-smallcaps" + android:gravity="center" + android:paddingTop="5dp" + android:paddingBottom="5dp" + android:textColor="?attr/secondaryTextColor" + android:textSize="@dimen/heading_font_text_view" + android:textStyle="bold" /> - - - + - - + diff --git a/app/src/full/res/layout/list_item_app_conn_details.xml b/app/src/full/res/layout/list_item_app_conn_details.xml index 1bc134c9e..8da3b5d49 100644 --- a/app/src/full/res/layout/list_item_app_conn_details.xml +++ b/app/src/full/res/layout/list_item_app_conn_details.xml @@ -74,6 +74,8 @@ android:layout_marginStart="5dp" android:gravity="center_vertical" android:padding="2dp" + android:ellipsize="end" + android:maxLines="3" android:textSize="@dimen/default_font_text_view" /> diff --git a/app/src/main/java/com/celzero/bravedns/database/ConnectionTrackerDAO.kt b/app/src/main/java/com/celzero/bravedns/database/ConnectionTrackerDAO.kt index 861d3d641..2f750f1c5 100644 --- a/app/src/main/java/com/celzero/bravedns/database/ConnectionTrackerDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/ConnectionTrackerDAO.kt @@ -88,11 +88,6 @@ interface ConnectionTrackerDAO { ) fun getBlockedConnections(query: String): PagingSource - @Query( - "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid GROUP BY ipAddress, uid, port ORDER BY count DESC LIMIT 100 OFFSET 0" - ) - fun getLogsForAppWithLimit(uid: Int): PagingSource - @Query( "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid GROUP BY ipAddress, uid, port ORDER BY count DESC" ) @@ -103,11 +98,6 @@ interface ConnectionTrackerDAO { ) fun getAllLogsFiltered(uid: Int, query: String): PagingSource - @Query( - "SELECT uid, ipAddress, port, COUNT(ipAddress) as count, '' as flag, 0 as blocked, GROUP_CONCAT(DISTINCT dnsQuery) as appOrDnsName FROM ConnectionTracker WHERE uid = :uid and ipAddress like :ipAddress GROUP BY ipAddress, uid, port ORDER BY count DESC LIMIT 100 OFFSET 0" - ) - fun getLogsForAppFilteredWithLimit(uid: Int, ipAddress: String): PagingSource - @Query("select count(DISTINCT(ipAddress)) from ConnectionTracker where uid = :uid") fun getAppConnectionsCount(uid: Int): LiveData From 3706828c35cbc18d657c8410184f328b18e25851 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:01:15 +0530 Subject: [PATCH 67/81] ui: display 'No logs' ui in app-wise logs screen --- .../res/layout/activity_app_wise_logs.xml | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/activity_app_wise_logs.xml b/app/src/main/res/layout/activity_app_wise_logs.xml index 20ccbe301..a1938c749 100644 --- a/app/src/main/res/layout/activity_app_wise_logs.xml +++ b/app/src/main/res/layout/activity_app_wise_logs.xml @@ -3,10 +3,37 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:nestedScrollingEnabled="false" - android:orientation="vertical" android:background="?attr/background" - app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + Date: Mon, 1 Apr 2024 20:02:26 +0530 Subject: [PATCH 68/81] ui: incorporate unicode icons for lockdown and always-on --- .../bravedns/ui/activity/WgConfigDetailActivity.kt | 12 ++++++++++++ app/src/main/res/layout/activity_wg_detail.xml | 2 ++ .../res/layout/list_item_wg_general_interface.xml | 1 + .../main/res/layout/list_item_wg_one_interface.xml | 1 + 4 files changed, 16 insertions(+) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt index de6d33a9d..eb7a7ad89 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgConfigDetailActivity.kt @@ -102,6 +102,18 @@ class WgConfigDetailActivity : AppCompatActivity(R.layout.activity_wg_detail) { } private fun init() { + b.globalLockdownTitleTv.text = + getString( + R.string.two_argument_space, + getString(R.string.firewall_rule_global_lockdown), + getString(R.string.symbol_lockdown) + ) + b.catchAllTitleTv.text = + getString( + R.string.two_argument_space, + getString(R.string.catch_all_wg_dialog_title), + getString(R.string.symbol_lightening) + ) if (wgType.isDefault()) { b.wgHeaderTv.text = getString(R.string.lbl_advanced).replaceFirstChar(Char::titlecase) b.lockdownRl.visibility = View.VISIBLE diff --git a/app/src/main/res/layout/activity_wg_detail.xml b/app/src/main/res/layout/activity_wg_detail.xml index 9d0dbd5bc..f4bbd31d4 100644 --- a/app/src/main/res/layout/activity_wg_detail.xml +++ b/app/src/main/res/layout/activity_wg_detail.xml @@ -121,6 +121,7 @@ android:orientation="vertical"> diff --git a/app/src/main/res/layout/list_item_wg_one_interface.xml b/app/src/main/res/layout/list_item_wg_one_interface.xml index 4e701fe70..81b717c86 100644 --- a/app/src/main/res/layout/list_item_wg_one_interface.xml +++ b/app/src/main/res/layout/list_item_wg_one_interface.xml @@ -51,6 +51,7 @@ android:layout_marginEnd="3dp" android:maxLength="15" android:padding="5dp" + android:ellipsize="end" android:textAppearance="?attr/textAppearanceHeadline6" android:textColor="?attr/secondaryTextColor" /> From e911bfd0be019c07b75b8c436b1b3bc3469fc151 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:09:07 +0530 Subject: [PATCH 69/81] tun: remove the system dns, if it could not be set --- app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt index 81d15ca21..15210f455 100644 --- a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt +++ b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt @@ -867,8 +867,8 @@ class GoVpnAdapter : KoinComponent { Intra.setSystemDNS(tunnel, sysDnsStr) } catch (e: Exception) { // this is not expected to happen Log.e(LOG_TAG_VPN, "set system dns: could not parse system dns", e) - // see BraveVpnService#determineSystemDns() - Intra.setSystemDNS(tunnel, FALLBACK_DNS) + // remove the system dns, if it could not be set + tunnel.resolver.remove(Backend.System) } } From fd330ff4a95a4490ab9c023edb74e79f7c15ee10 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:10:49 +0530 Subject: [PATCH 70/81] vpn: enhance network change determination and more logging --- .../bravedns/service/BraveVPNService.kt | 183 ++++++++++++------ 1 file changed, 122 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index 215a847f5..ea6fdd400 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -52,7 +52,6 @@ import backend.RDNS import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.data.AppConfig -import com.celzero.bravedns.data.AppConfig.Companion.FALLBACK_DNS import com.celzero.bravedns.data.ConnTrackerMetaData import com.celzero.bravedns.data.ConnectionSummary import com.celzero.bravedns.database.AppInfo @@ -198,9 +197,9 @@ class BraveVPNService : private var accessibilityListener: AccessibilityManager.AccessibilityStateChangeListener? = null data class OverlayNetworks( - var has4: Boolean = false, - var has6: Boolean = false, - var failOpen: Boolean = true + val has4: Boolean = false, + val has6: Boolean = false, + val failOpen: Boolean = true ) data class Networks( @@ -379,6 +378,7 @@ class BraveVPNService : } if (unknownAppBlocked(uid)) { + logd("firewall: unknown app blocked, $uid") return FirewallRuleset.RULE5 } @@ -386,6 +386,7 @@ class BraveVPNService : if (appStatus.isUntracked()) { io("addNewApp") { rdb.addNewApp(uid) } if (newAppBlocked(uid)) { + logd("firewall: new app blocked, $uid") return FirewallRuleset.RULE1B } } @@ -393,19 +394,23 @@ class BraveVPNService : // check for app rules (unmetered, metered connections) val appRuleset = appBlocked(connInfo, connectionStatus) if (appRuleset != null) { + logd("firewall: app blocked, $uid") return appRuleset } if (VpnController.isVpnLockdown() && isAppPaused()) { + logd("firewall: lockdown, app paused, $uid") // allow when firewall is paused: as a placeholder RULE8(bypass app) is used return FirewallRuleset.RULE8 } when (getDomainRule(connInfo.query, uid)) { DomainRulesManager.Status.BLOCK -> { + logd("firewall: domain blocked, $uid") return FirewallRuleset.RULE2E } DomainRulesManager.Status.TRUST -> { + logd("firewall: domain trusted, $uid") return FirewallRuleset.RULE2F } DomainRulesManager.Status.NONE -> { @@ -416,9 +421,11 @@ class BraveVPNService : // IP rules when (uidIpStatus(uid, connInfo.destIP, connInfo.destPort)) { IpRulesManager.IpRuleStatus.BLOCK -> { + logd("firewall: ip blocked, $uid") return FirewallRuleset.RULE2 } IpRulesManager.IpRuleStatus.TRUST -> { + logd("firewall: ip trusted, $uid") return FirewallRuleset.RULE2B } IpRulesManager.IpRuleStatus.BYPASS_UNIVERSAL -> { @@ -432,11 +439,13 @@ class BraveVPNService : // by-pass dns firewall, go-through app specific ip and domain rules before applying if (appStatus.bypassDnsFirewall()) { + logd("firewall: bypass dns firewall, $uid") return FirewallRuleset.RULE1H } // isolate mode if (appStatus.isolate()) { + logd("firewall: isolate mode, $uid") return FirewallRuleset.RULE1G } @@ -447,12 +456,15 @@ class BraveVPNService : // bypass universal should block the domains that are blocked by dns (local/remote) // unless the domain is trusted by the user if (anyRealIpBlocked && globalDomainRule != DomainRulesManager.Status.TRUST) { + logd("firewall: bypass universal, dns blocked, $uid, ${connInfo.query}") return FirewallRuleset.RULE2G } return if (dnsProxied(connInfo.destPort)) { + logd("firewall: bypass universal, dns proxied, $uid") FirewallRuleset.RULE9 } else { + logd("firewall: bypass universal, $uid") FirewallRuleset.RULE8 } } @@ -460,9 +472,11 @@ class BraveVPNService : // check for global domain allow/block domains when (globalDomainRule) { DomainRulesManager.Status.TRUST -> { + logd("firewall: global domain trusted, $uid, ${connInfo.query}") return FirewallRuleset.RULE2I } DomainRulesManager.Status.BLOCK -> { + logd("firewall: global domain blocked, $uid, ${connInfo.query}") return FirewallRuleset.RULE2H } else -> { @@ -473,9 +487,11 @@ class BraveVPNService : // should ip rules by-pass or block universal firewall rules when (globalIpStatus(connInfo.destIP, connInfo.destPort)) { IpRulesManager.IpRuleStatus.BLOCK -> { + logd("firewall: global ip blocked, $uid, ${connInfo.destIP}") return FirewallRuleset.RULE2D } IpRulesManager.IpRuleStatus.BYPASS_UNIVERSAL -> { + logd("firewall: global ip bypass universal, $uid, ${connInfo.destIP}") return FirewallRuleset.RULE2C } IpRulesManager.IpRuleStatus.TRUST -> { @@ -489,6 +505,7 @@ class BraveVPNService : // if any of the real ip is blocked then allow only if it is trusted, // otherwise no need to check further if (anyRealIpBlocked) { + logd("firewall: dns blocked, $uid, ${connInfo.query}") return FirewallRuleset.RULE2G } else { // no-op; pass-through @@ -497,37 +514,45 @@ class BraveVPNService : val isMetered = isConnectionMetered(connInfo.destIP) // block all metered connections (Universal firewall setting) if (persistentState.getBlockMeteredConnections() && isMetered) { + logd("firewall: metered blocked, $uid") return FirewallRuleset.RULE1F } // block apps when universal lockdown is enabled if (universalLockdown()) { + logd("firewall: universal lockdown, $uid") return FirewallRuleset.RULE11 } if (httpBlocked(connInfo.destPort)) { + logd("firewall: http blocked, $uid") return FirewallRuleset.RULE10 } if (deviceLocked()) { + logd("firewall: device locked, $uid") return FirewallRuleset.RULE3 } if (udpBlocked(uid, connInfo.protocol, connInfo.destPort)) { + logd("firewall: udp blocked, $uid") return FirewallRuleset.RULE6 } if (blockBackgroundData(uid)) { + logd("firewall: background data blocked, $uid") return FirewallRuleset.RULE4 } // if all packets on port 53 needs to be trapped if (dnsProxied(connInfo.destPort)) { + logd("firewall: dns proxied, $uid") return FirewallRuleset.RULE9 } // if connInfo.query is empty, then it is not resolved by user set dns if (dnsBypassed(connInfo.query)) { + logd("firewall: dns bypassed, $uid") return FirewallRuleset.RULE7 } } catch (iex: Exception) { @@ -1315,9 +1340,9 @@ class BraveVPNService : // have app, ip, domain rules. See RefreshDatabase#refresh rdb.refresh(RefreshDatabase.ACTION_REFRESH_AUTO) { restartVpn(opts, Networks(null, overlayNetworks), why = "startVpn") - controllerProto = Pair(overlayNetworks.has4, overlayNetworks.has6) + vpnProtos = Pair(overlayNetworks.has4, overlayNetworks.has6) // update the controller, which will update the UI (home screen btm sheet) - VpnController.updateProtocol(controllerProto) + VpnController.updateProtocol(vpnProtos) // call this *after* a new vpn is created #512 uiCtx("observers") { observeChanges() } } @@ -1489,6 +1514,7 @@ class BraveVPNService : notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) } PersistentState.NETWORK -> { + Log.i(LOG_TAG_VPN, "network change, ${persistentState.useMultipleNetworks}") io("useAllNetworks") { notifyConnectionMonitor() } } PersistentState.NOTIFICATION_ACTION -> { @@ -1524,6 +1550,10 @@ class BraveVPNService : restartVpnWithNewAppConfig(reason = "routeRethinkInRethink") } } + PersistentState.CONNECTIVITY_CHECKS -> { + Log.i(LOG_TAG_VPN, "connectivity checks changed, ${persistentState.connectivityChecks}") + io("connectivityChecks") { notifyConnectionMonitor() } + } } } @@ -1569,7 +1599,7 @@ class BraveVPNService : } private suspend fun handleIPProtoChanges() { - logd("handle ip proto changes") + Log.i(LOG_TAG_VPN, "handle ip proto changes") if (InternetProtocol.isAuto(persistentState.internetProtocolType)) { // initiates connectivity checks if Auto mode and calls onNetworkConnected // or onNetworkDisconnected. onNetworkConnected may call restartVpn and setRoute on @@ -1583,7 +1613,7 @@ class BraveVPNService : private suspend fun handleProxyChange() { val tunProxyMode = appConfig.getTunProxyMode() val proxy = AppConfig.ProxyProvider.getProxyProvider(appConfig.getProxyProvider()) - logd("handle proxy change, proxy: ${proxy.name}") + Log.i(LOG_TAG_VPN, "handle proxy change, proxy: $proxy, mode: $tunProxyMode") when (proxy) { AppConfig.ProxyProvider.NONE -> { // no-op @@ -1654,7 +1684,7 @@ class BraveVPNService : reason ) // update the controller, which will update the UI (home screen btm sheet) - VpnController.updateProtocol(controllerProto) + VpnController.updateProtocol(vpnProtos) } private suspend fun setPcapMode() { @@ -1886,50 +1916,64 @@ class BraveVPNService : ) private fun interestingNetworkChanges( - old: ConnectionMonitor.UnderlyingNetworks?, - new: ConnectionMonitor.UnderlyingNetworks + old: ConnectionMonitor.UnderlyingNetworks? = underlyingNetworks, + _new: ConnectionMonitor.UnderlyingNetworks? = null, + aux: OverlayNetworks = overlayNetworks ): NetworkChanges { + var new = _new + // when old and new are null, no changes + if (old == null && new == null) { + return NetworkChanges(false, false, false) + } // no old routes to compare with, return true if (old == null) return NetworkChanges() + if (new == null) { + // new is null, but old is not, then check for changes in aux networks + new = old + } val mtuChanged = old.minMtu != new.minMtu + + // val auxHas4 = aux.has4 || aux.failOpen + // val auxHas6 = aux.has6 || aux.failOpen + val n = Networks(new, aux) + val (tunHas4, tunHas6) = vpnProtos // current tunnel routes v4/v6? + val (tunWants4, tunWants6) = determineRoutes(n) + + val ok4 = tunHas4 == tunWants4 // old & new agree on activ capable of routing ipv4 or not + val ok6 = tunHas6 == tunWants6 // old & new agree on activ capable of routing ipv6 or not + val routesChanged = !ok4 || !ok6 + if (new.useActive) { connectivityManager.activeNetwork?.let { activ -> - val oldHas4 = controllerProto.first // current tunnel routes ipv4? - val oldHas6 = controllerProto.second // current tunnel routes ipv6? + // val tunWants4 = activHas4 && auxHas4 + // val tunWants6 = activHas6 && auxHas6 val activHas4 = isNetworkSame(new.ipv4Net.firstOrNull()?.network, activ) val activHas6 = isNetworkSame(new.ipv6Net.firstOrNull()?.network, activ) - val both4 = - oldHas4 == activHas4 // old & new agree on activ capable of routing ipv4 or not - val both6 = - oldHas6 == activHas6 // old & new agree on activ capable of routing ipv6 or not - val routesChanged = !both4 || !both6 - - val oldnet4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, activ) - val oldnet6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, activ) - val bothnet4 = - oldnet4 == activHas4 // routing for ipv4 is same in old and new FIRST network - val bothnet6 = - oldnet6 == activHas6 // routing for ipv6 is same in old and new FIRST network - val netChanged = !bothnet4 || !bothnet6 + val oldActivHas4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, activ) + val oldActivHas6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, activ) + val okActiv4 = + oldActivHas4 == activHas4 // routing for ipv4 is same in old and new FIRST network + val okActiv6 = + oldActivHas6 == activHas6 // routing for ipv6 is same in old and new FIRST network + val netChanged = !okActiv4 || !okActiv6 // for active networks, changes in routes includes all possible network changes; return NetworkChanges(routesChanged, netChanged, mtuChanged) - } - // active network unknown, fallthrough + } // active network null, fallthrough to check for netChanged } - // check if ipv6 or ipv4 routes are different in old and new networks - val old6 = old.ipv6Net.isNotEmpty() - val new6 = new.ipv6Net.isNotEmpty() - val old4 = old.ipv4Net.isNotEmpty() - val new4 = new.ipv4Net.isNotEmpty() - val routesChanged = old6 != new6 || old4 != new4 + // val oldHas6 = old.ipv6Net.isNotEmpty() || tunHas6 + // val oldHas4 = old.ipv4Net.isNotEmpty() || tunHas4 + // val newHas6 = new.ipv6Net.isNotEmpty() + // val newHas4 = new.ipv4Net.isNotEmpty() + // val tunWants4 = newHas4 && auxHas4 + // val tunWants6 = newHas6 && auxHas6 // check if the first networks are different to urge rebinds where necessary (ex: WireGuard) - val oldnet6 = old.ipv6Net.firstOrNull()?.network - val newnet6 = new.ipv6Net.firstOrNull()?.network - val oldnet4 = old.ipv4Net.firstOrNull()?.network - val newnet4 = new.ipv4Net.firstOrNull()?.network - val netChanged = !isNetworkSame(oldnet6, newnet6) || !isNetworkSame(oldnet4, newnet4) + val oldFirst6 = old.ipv6Net.firstOrNull()?.network + val newFirst6 = new.ipv6Net.firstOrNull()?.network + val oldFirst4 = old.ipv4Net.firstOrNull()?.network + val newFirst4 = new.ipv4Net.firstOrNull()?.network + val netChanged = !isNetworkSame(oldFirst6, newFirst6) || !isNetworkSame(oldFirst4, newFirst4) return NetworkChanges(routesChanged, netChanged, mtuChanged) } @@ -2023,9 +2067,8 @@ class BraveVPNService : val list = dnsServers?.map { it.hostAddress ?: "" }?.filter { it != "" } if (list.isNullOrEmpty()) { - // regardless of rinr loopback mode, always use LOOPBACK_DNS to avoid leaks - Log.w(LOG_TAG_VPN, "No System DNS servers; unsetting existing $FALLBACK_DNS") - dnsList.add(FALLBACK_DNS) + // no dns servers found, return empty list + return emptyList() } else { dnsList.addAll(list) } @@ -2185,28 +2228,45 @@ class BraveVPNService : } // var to update the controller with the protocol set for the vpn - private var controllerProto: Pair = Pair(false, false) + private var vpnProtos: Pair = Pair(false, false) + + private fun determineRoutes(n: Networks): Pair { + var has6 = route6(n) + var has4 = route4(n) + + if (!has4 && !has6 && !n.overlayNws.failOpen) { + // When overlay networks has v6 routes but active network has v4 routes + // both has4 and has6 will be false and fail-open may open up BOTH routes + // What's desirable is for the active network route to take precedence, that is, + // to only add v4 route in case of a mismatch. Failing open will falsely make + // apps think the underlying active network is dual-stack when it is not causing + // all sorts of delays (due to happy eyeballs). + val n2 = Networks(n.underlyingNws, /*fail-open overlay*/ OverlayNetworks()) + has4 = route4(n2) + has6 = route6(n2) + } + if (!has4 && !has6) { + // no route available for both v4 and v6, add all routes + // connectivity manager is expected to retry when no route is available + // see ConnectionMonitor#repopulateTrackedNetworks + Log.i(LOG_TAG_VPN, "No routes, fail-open? $FAIL_OPEN_ON_NO_NETWORK") + has4 = FAIL_OPEN_ON_NO_NETWORK + has6 = FAIL_OPEN_ON_NO_NETWORK + } else { + Log.i(LOG_TAG_VPN, "Building vpn for v4? $has4, v6? $has6") + } + + return Pair(has4, has6) + } private suspend fun establishVpn(networks: Networks): Pair { try { val mtu = mtu() // get mtu from the underlyingnetworks var builder: VpnService.Builder = newBuilder().setSession("Rethink").setMtu(mtu) - var has6 = route6(networks) - var has4 = route4(networks) - val nwProto = InternetProtocol.byProtos(has4, has6).value() - controllerProto = Pair(has4, has6) - - Log.i(LOG_TAG_VPN, "Building vpn for v4? $has4, v6? $has6") + val (has4, has6) = determineRoutes(networks) - if (!has4 && !has6) { - // no route available for both v4 and v6, add all routes - // connectivity manager is expected to retry when no route is available - // see ConnectionMonitor#repopulateTrackedNetworks - Log.i(LOG_TAG_VPN, "No route available, fail-opn? $FAIL_OPEN_ON_NO_NETWORK") - has4 = FAIL_OPEN_ON_NO_NETWORK - has6 = FAIL_OPEN_ON_NO_NETWORK - } + vpnProtos = Pair(has4, has6) // setup the gateway addr if (has4) { @@ -2247,7 +2307,7 @@ class BraveVPNService : } } val fd = builder.establish() - return Pair(fd, nwProto) + return Pair(fd, InternetProtocol.byProtos(has4, has6).value()) } catch (e: Exception) { Log.e(LOG_TAG_VPN, e.message, e) // default to Settings.Ns46 @@ -2311,9 +2371,10 @@ class BraveVPNService : private fun onOverlayNetworkChanged(nw: OverlayNetworks) { // compare the overlay network pair with the overlayNetworkIpStates to determine if the // overlay network is changed, if so, restart the vpn - val isRoutesChanged = overlayNetworks != nw + val interestingNet = interestingNetworkChanges(aux = nw) + val isRoutesChanged = interestingNet.routesChanged + overlayNetworks = nw if (isRoutesChanged) { - overlayNetworks = nw Log.i(LOG_TAG_VPN, "overlay changed $overlayNetworks, restart vpn") // There may be cases where both overlay and underlay networks have the same routes. // In such scenarios, no restart is required. However, here the routeChange is @@ -2327,7 +2388,7 @@ class BraveVPNService : ) } } else { - Log.i(LOG_TAG_VPN, "overlay network not changed, no restart needed") + Log.i(LOG_TAG_VPN, "routes not changed, no restart needed") } } From 59b52256e8e68a2b7f762aaa7bff76444592fcb0 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:11:56 +0530 Subject: [PATCH 71/81] introduce setting to disable default connectivity checks --- .../ui/activity/TunnelSettingsActivity.kt | 11 ++++ .../res/layout/activity_tunnel_settings.xml | 56 ++++++++++++++++++ .../bravedns/service/ConnectionMonitor.kt | 59 +++++++++---------- .../bravedns/service/PersistentState.kt | 4 ++ 4 files changed, 99 insertions(+), 31 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt index 2a838446c..942f613b3 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt @@ -69,6 +69,8 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin b.settingsActivityAllNetworkSwitch.isChecked = persistentState.useMultipleNetworks // route lan traffic b.settingsActivityLanTrafficSwitch.isChecked = persistentState.privateIps + // connectivity check + b.settingsActivityConnectivityChecksSwitch.isChecked = persistentState.connectivityChecks // for protocol translation, enable only on DNS/DNS+Firewall mode if (appConfig.getBraveMode().isDnsActive()) { b.settingsActivityPtransSwitch.isChecked = persistentState.protocolTranslationType @@ -193,6 +195,15 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin } b.settingsActivityDefaultDnsRl.setOnClickListener { showDefaultDnsDialog() } + + b.settingsActivityConnectivityChecksRl.setOnClickListener { + b.settingsActivityConnectivityChecksSwitch.isChecked = + !b.settingsActivityConnectivityChecksSwitch.isChecked + } + + b.settingsActivityConnectivityChecksSwitch.setOnCheckedChangeListener { _, isChecked -> + persistentState.connectivityChecks = isChecked + } } private fun showDefaultDnsDialog() { diff --git a/app/src/full/res/layout/activity_tunnel_settings.xml b/app/src/full/res/layout/activity_tunnel_settings.xml index a03a564c2..2cf0911ba 100644 --- a/app/src/full/res/layout/activity_tunnel_settings.xml +++ b/app/src/full/res/layout/activity_tunnel_settings.xml @@ -177,6 +177,62 @@ + + + + + + + + + + + + + + + = mutableSetOf() + // add cellular, wifi, bluetooth, ethernet, vpn, wifi aware, low pan private val networkRequest: NetworkRequest = - if (androidValidatedNetworks) - NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) - .apply { if (isAtleastS()) setIncludeOtherUidNetworks(true) } - .build() - else - // add cellular, wifi, bluetooth, ethernet, vpn, wifi aware, low pan NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .apply { if (isAtleastS()) setIncludeOtherUidNetworks(true) } - // api27: .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) - // api26: .addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN) - .build() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) + .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + .apply { if (isAtleastS()) setIncludeOtherUidNetworks(true) } + // api27: .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) + // api26: .addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN) + .build() + + /* + // android validated networks builder + NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .apply { if (isAtleastS()) setIncludeOtherUidNetworks(true) } + .build() + + */ // An Android handler thread internally operates on a looper // ref: @@ -122,8 +123,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : val msgType: Int, val networkSet: Set, val isForceUpdate: Boolean, - val testReachability: Boolean, - val dualStack: Boolean + val testReachability: Boolean ) init { @@ -213,15 +213,13 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : isForceUpdate: Boolean = false, delay: Long = TimeUnit.SECONDS.toMillis(1) ) { - val isDualStack = InternetProtocol.isAuto(persistentState.internetProtocolType) - val testReachability = isDualStack && !androidValidatedNetworks + val testReachability = persistentState.connectivityChecks val msg = constructNetworkMessage( if (persistentState.useMultipleNetworks) MSG_ADD_ALL_NETWORKS else MSG_ADD_ACTIVE_NETWORK, isForceUpdate, - testReachability, - isDualStack + testReachability ) serviceHandler?.removeMessages(MSG_ADD_ACTIVE_NETWORK, null) serviceHandler?.removeMessages(MSG_ADD_ALL_NETWORKS, null) @@ -236,10 +234,9 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : private fun constructNetworkMessage( what: Int, isForceUpdate: Boolean, - testReachability: Boolean, - dualStack: Boolean + testReachability: Boolean ): Message { - val opPrefs = OpPrefs(what, networkSet, isForceUpdate, testReachability, dualStack) + val opPrefs = OpPrefs(what, networkSet, isForceUpdate, testReachability) val message = Message.obtain() message.what = what message.obj = opPrefs @@ -260,7 +257,7 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : val minMtu: Int, var isActiveNetworkMetered: Boolean, // may be updated by client listener var lastUpdated: Long, // may be updated by client listener - val dnsServers: LinkedHashMap + val dnsServers: Map ) // Handles the network messages from the callback from the connectivity manager @@ -393,7 +390,8 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : determineMtu(useActiveNetwork), isActiveNetworkMetered, SystemClock.elapsedRealtime(), - dnsServers) + Collections.unmodifiableMap(dnsServers) + ) if (DEBUG) { trackedIpv4Networks.forEach { Log.d(LOG_TAG_CONNECTION, "inform4: ${it.network}, ${it.networkType}, $sz") @@ -488,7 +486,6 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : networks: LinkedHashSet ) { val testReachability: Boolean = opPrefs.testReachability - val dualStack: Boolean = opPrefs.dualStack val activeNetwork = connectivityManager.activeNetwork // null in vpn lockdown mode diff --git a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt index 6af8aa9fb..d1ab4940d 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt @@ -55,6 +55,7 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { const val PRIVATE_IPS = "private_ips" const val RETHINK_IN_RETHINK = "route_rethink_in_rethink" const val PREVENT_DNS_LEAKS = "prevent_dns_leaks" + const val CONNECTIVITY_CHECKS = "connectivity_check" } // when vpn is started by the user, this is set to true; set to false when user stops @@ -268,6 +269,9 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { // route rethink in rethink var routeRethinkInRethink by booleanPref("route_rethink_in_rethink").withDefault(false) + // perform connectivity checks + var connectivityChecks by booleanPref("connectivity_check").withDefault(!Utilities.isPlayStoreFlavour()) + // proxy dns requests over proxy var proxyDns by booleanPref("proxy_dns").withDefault(true) From 06f7901ce34cca9b030ec06806749f697cd365a7 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:12:42 +0530 Subject: [PATCH 72/81] IDE suggestion: Improve build performance --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bff0ec992..7a169a185 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,6 +23,6 @@ kotlin.code.style=official android.nonTransitiveRClass=true # Enable configuration cache org.gradle.unsafe.configuration-cache=true -android.nonFinalResIds=false +android.nonFinalResIds=true # Version code for this module (34 for v055c) VERSION_CODE=34 From 46e81f29248cc94daa9c72bba833856cee425cd4 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 20:12:56 +0530 Subject: [PATCH 73/81] bump firestack version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6481e915f..a9d0a0829 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,8 +247,8 @@ dependencies { fullImplementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9' // from: https://jitpack.io/#celzero/firestack - download 'com.github.celzero:firestack:95d51bcbcc@aar' - implementation 'com.github.celzero:firestack:95d51bcbcc@aar' + download 'com.github.celzero:firestack:f5a2218ed6@aar' + implementation 'com.github.celzero:firestack:f5a2218ed6@aar' // Work manager implementation('androidx.work:work-runtime-ktx:2.9.0') { From d83c840be4ab9983c98c9ef1127205849f4cb6b7 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 22:09:09 +0530 Subject: [PATCH 74/81] Fix: #1320; show notification upon granting notification permission to the app again Fix: https://github.com/celzero/rethink-app/issues/1320 --- .../celzero/bravedns/ui/activity/MiscSettingsActivity.kt | 7 ++++++- .../celzero/bravedns/ui/fragment/HomeScreenFragment.kt | 2 +- .../java/com/celzero/bravedns/service/BraveVPNService.kt | 8 ++++++++ .../java/com/celzero/bravedns/service/PersistentState.kt | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/MiscSettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/MiscSettingsActivity.kt index a9f086ac4..131d5697c 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/MiscSettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/MiscSettingsActivity.kt @@ -556,13 +556,13 @@ class MiscSettingsActivity : AppCompatActivity(R.layout.activity_misc_settings) // Sets up permissions request launcher. notificationPermissionResult = registerForActivityResult(ActivityResultContracts.RequestPermission()) { + persistentState.shouldRequestNotificationPermission = it if (it) { Log.i(LOG_TAG_VPN, "User allowed notification permission for the app") b.settingsActivityAppNotificationRl.visibility = View.VISIBLE b.settingsActivityAppNotificationSwitch.isChecked = true } else { Log.w(LOG_TAG_VPN, "User rejected notification permission for the app") - persistentState.shouldRequestNotificationPermission = false b.settingsActivityAppNotificationRl.visibility = View.VISIBLE b.settingsActivityAppNotificationSwitch.isChecked = false invokeAndroidNotificationSetting() @@ -762,9 +762,14 @@ class MiscSettingsActivity : AppCompatActivity(R.layout.activity_misc_settings) // notification permission is granted to the app, enable switch b.settingsActivityAppNotificationRl.visibility = View.VISIBLE b.settingsActivityAppNotificationSwitch.isChecked = true + if (!persistentState.shouldRequestNotificationPermission) { + // if the user has already granted the permission, set the flag to true + persistentState.shouldRequestNotificationPermission = true + } } else { b.settingsActivityAppNotificationRl.visibility = View.VISIBLE b.settingsActivityAppNotificationSwitch.isChecked = false + persistentState.shouldRequestNotificationPermission = false } b.settingsActivityAppNotificationPersistentRl.visibility = View.VISIBLE b.settingsActivityAppNotificationPersistentSwitch.isChecked = diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index 556a2817f..12789821a 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -1099,10 +1099,10 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { // Sets up permissions request launcher. notificationPermissionResult = registerForActivityResult(ActivityResultContracts.RequestPermission()) { + persistentState.shouldRequestNotificationPermission = it if (it) { Log.i(LOG_TAG_UI, "User accepted notification permission") } else { - persistentState.shouldRequestNotificationPermission = false Log.w(LOG_TAG_UI, "User rejected notification permission") Snackbar.make( requireActivity().findViewById(android.R.id.content).rootView, diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index ea6fdd400..9e336c901 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -1554,6 +1554,14 @@ class BraveVPNService : Log.i(LOG_TAG_VPN, "connectivity checks changed, ${persistentState.connectivityChecks}") io("connectivityChecks") { notifyConnectionMonitor() } } + PersistentState.NOTIFICATION_PERMISSION -> { + if (persistentState.shouldRequestNotificationPermission) { + Log.i(LOG_TAG_VPN, "notification permission allowed, show notification") + notificationManager.notify(SERVICE_ID, updateNotificationBuilder()) + } else { + // no-op + } + } } } diff --git a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt index d1ab4940d..1ff9a6bea 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt @@ -56,6 +56,7 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { const val RETHINK_IN_RETHINK = "route_rethink_in_rethink" const val PREVENT_DNS_LEAKS = "prevent_dns_leaks" const val CONNECTIVITY_CHECKS = "connectivity_check" + const val NOTIFICATION_PERMISSION = "notification_permission_request" } // when vpn is started by the user, this is set to true; set to false when user stops From 560d3660320fddeeca54b76614123a6d4ccf71a3 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Mon, 1 Apr 2024 22:10:10 +0530 Subject: [PATCH 75/81] ui: show connectivity checks only on auto mode --- .../ui/activity/TunnelSettingsActivity.kt | 4 + .../res/layout/activity_tunnel_settings.xml | 113 +++++++++--------- .../bravedns/service/ConnectionMonitor.kt | 5 +- .../res/drawable/ic_connectivity_checks.xml | 20 ++++ 4 files changed, 84 insertions(+), 58 deletions(-) create mode 100644 app/src/main/res/drawable/ic_connectivity_checks.xml diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt index 942f613b3..bd1811072 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/TunnelSettingsActivity.kt @@ -233,6 +233,7 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin getString(R.string.settings_ip_text_ipv4) ) b.settingsActivityPtransRl.visibility = View.GONE + b.settingsActivityConnectivityChecksRl.visibility = View.GONE } InternetProtocol.IPv6.id -> { b.genSettingsIpDesc.text = @@ -241,6 +242,7 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin getString(R.string.settings_ip_text_ipv6) ) b.settingsActivityPtransRl.visibility = View.VISIBLE + b.settingsActivityConnectivityChecksRl.visibility = View.GONE } InternetProtocol.IPv46.id -> { b.genSettingsIpDesc.text = @@ -249,6 +251,7 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin getString(R.string.settings_ip_text_ipv46) ) b.settingsActivityPtransRl.visibility = View.GONE + b.settingsActivityConnectivityChecksRl.visibility = View.VISIBLE } else -> { b.genSettingsIpDesc.text = @@ -257,6 +260,7 @@ class TunnelSettingsActivity : AppCompatActivity(R.layout.activity_tunnel_settin getString(R.string.settings_ip_text_ipv4) ) b.settingsActivityPtransRl.visibility = View.GONE + b.settingsActivityConnectivityChecksRl.visibility = View.GONE } } } diff --git a/app/src/full/res/layout/activity_tunnel_settings.xml b/app/src/full/res/layout/activity_tunnel_settings.xml index 2cf0911ba..efeb02de5 100644 --- a/app/src/full/res/layout/activity_tunnel_settings.xml +++ b/app/src/full/res/layout/activity_tunnel_settings.xml @@ -177,63 +177,6 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt index 9561525e3..095b8d17a 100644 --- a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt +++ b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt @@ -31,6 +31,7 @@ import android.system.ErrnoException import android.system.OsConstants.ECONNREFUSED import android.util.Log import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG +import com.celzero.bravedns.util.InternetProtocol import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_CONNECTION import com.celzero.bravedns.util.Utilities.isAtleastQ import com.celzero.bravedns.util.Utilities.isAtleastS @@ -213,7 +214,9 @@ class ConnectionMonitor(context: Context, networkListener: NetworkListener) : isForceUpdate: Boolean = false, delay: Long = TimeUnit.SECONDS.toMillis(1) ) { - val testReachability = persistentState.connectivityChecks + val dualStack = + InternetProtocol.getInternetProtocol(persistentState.internetProtocolType).isIPv46() + val testReachability = dualStack && persistentState.connectivityChecks val msg = constructNetworkMessage( if (persistentState.useMultipleNetworks) MSG_ADD_ALL_NETWORKS diff --git a/app/src/main/res/drawable/ic_connectivity_checks.xml b/app/src/main/res/drawable/ic_connectivity_checks.xml new file mode 100644 index 000000000..6cf868ac7 --- /dev/null +++ b/app/src/main/res/drawable/ic_connectivity_checks.xml @@ -0,0 +1,20 @@ + + + + From ce91cddcde391e779a4083fc66ec61d377b94ff1 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:20:43 +0530 Subject: [PATCH 76/81] ui: centered rethink title in home screen --- .../bravedns/ui/fragment/HomeScreenFragment.kt | 5 +++++ app/src/full/res/layout/fragment_home_screen.xml | 13 +++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index 12789821a..a6b2772f8 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -189,6 +189,11 @@ class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { startActivity(intent) } + b.fhsTitleRethink.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW, RETHINKDNS_SPONSOR_LINK.toUri()) + startActivity(intent) + } + // comment out the below code to disable the alerts card (v0.5.5b) // b.fhsCardAlertsLl.setOnClickListener { startActivity(ScreenType.ALERTS) } } diff --git a/app/src/full/res/layout/fragment_home_screen.xml b/app/src/full/res/layout/fragment_home_screen.xml index 033004ea6..42dbd0771 100644 --- a/app/src/full/res/layout/fragment_home_screen.xml +++ b/app/src/full/res/layout/fragment_home_screen.xml @@ -18,9 +18,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingStart="15dp" android:paddingTop="15dp" - android:paddingEnd="15dp" + android:layout_gravity="center" android:visibility="visible"> From 72e01cc2478ccf9104ed78fc9cd42bf8a3bb259e Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:21:15 +0530 Subject: [PATCH 77/81] bump version code for v055d(35) --- app/src/main/AndroidManifest.xml | 4 ++-- gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea25a54ae..37ad32e57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="35" + android:versionName="v055d"> diff --git a/gradle.properties b/gradle.properties index 7a169a185..73bf7eabb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,5 +24,5 @@ android.nonTransitiveRClass=true # Enable configuration cache org.gradle.unsafe.configuration-cache=true android.nonFinalResIds=true -# Version code for this module (34 for v055c) -VERSION_CODE=34 +# Version code for this module (35 for v055d) +VERSION_CODE=35 From 9bce5e646bac6db9d1e14ba045826d1ad79ce50e Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:21:39 +0530 Subject: [PATCH 78/81] tun: check for dns port in bind4 and bind6 --- .../bravedns/service/BraveVPNService.kt | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index 9e336c901..60fdfc737 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -242,7 +242,9 @@ class BraveVPNService : var pfd: ParcelFileDescriptor? = null try { // split the addrPort to get the IP address and convert it to InetAddress - val destIp = IPAddressString(IpRulesManager.splitHostPort(addrPort).first).address + val dest = IpRulesManager.splitHostPort(addrPort) + val destIp = IPAddressString(dest.first).address + val destPort = dest.second.toIntOrNull() // in case of zero, bind only for wg connections, wireguard tries to bind to // network with zero addresses if ( @@ -255,7 +257,9 @@ class BraveVPNService : } val destAddr = destIp.toInetAddress() pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) - val net = curnet?.dnsServers?.get(destAddr) + // check if the destination port is DNS port, if so bind to the network where the dns + // belongs to, else bind to the available network + val net = if (KnownPorts.isDns(destPort)) curnet?.dnsServers?.get(destAddr) else null if (net != null) { try { logd("bind4: $who, $addrPort, $fid, ${net.networkHandle}") @@ -305,12 +309,15 @@ class BraveVPNService : var pfd: ParcelFileDescriptor? = null try { // split the addrPort to get the IP address and convert it to InetAddress - val destAddr = - IPAddressString(IpRulesManager.splitHostPort(addrPort).first) - .address - .toInetAddress() + val dest = IpRulesManager.splitHostPort(addrPort) + val destIp = IPAddressString(dest.first).address + val destPort = dest.second.toIntOrNull() + val destAddr = destIp.toInetAddress() + pfd = ParcelFileDescriptor.adoptFd(fid.toInt()) - val net = curnet?.dnsServers?.get(destAddr) + // check if the destination port is DNS port, if so bind to the network where the dns + // belongs to, else bind to the available network + val net = if (KnownPorts.isDns(destPort)) curnet?.dnsServers?.get(destAddr) else null if (net != null) { try { logd("bind6: $who, $addrPort, $fid, ${net.networkHandle}") @@ -1551,7 +1558,10 @@ class BraveVPNService : } } PersistentState.CONNECTIVITY_CHECKS -> { - Log.i(LOG_TAG_VPN, "connectivity checks changed, ${persistentState.connectivityChecks}") + Log.i( + LOG_TAG_VPN, + "connectivity checks changed, ${persistentState.connectivityChecks}" + ) io("connectivityChecks") { notifyConnectionMonitor() } } PersistentState.NOTIFICATION_PERMISSION -> { @@ -1961,13 +1971,15 @@ class BraveVPNService : val oldActivHas4 = isNetworkSame(old.ipv4Net.firstOrNull()?.network, activ) val oldActivHas6 = isNetworkSame(old.ipv6Net.firstOrNull()?.network, activ) val okActiv4 = - oldActivHas4 == activHas4 // routing for ipv4 is same in old and new FIRST network + oldActivHas4 == + activHas4 // routing for ipv4 is same in old and new FIRST network val okActiv6 = - oldActivHas6 == activHas6 // routing for ipv6 is same in old and new FIRST network + oldActivHas6 == + activHas6 // routing for ipv6 is same in old and new FIRST network val netChanged = !okActiv4 || !okActiv6 // for active networks, changes in routes includes all possible network changes; return NetworkChanges(routesChanged, netChanged, mtuChanged) - } // active network null, fallthrough to check for netChanged + } // active network null, fallthrough to check for netChanged } // check if ipv6 or ipv4 routes are different in old and new networks // val oldHas6 = old.ipv6Net.isNotEmpty() || tunHas6 @@ -1981,7 +1993,8 @@ class BraveVPNService : val newFirst6 = new.ipv6Net.firstOrNull()?.network val oldFirst4 = old.ipv4Net.firstOrNull()?.network val newFirst4 = new.ipv4Net.firstOrNull()?.network - val netChanged = !isNetworkSame(oldFirst6, newFirst6) || !isNetworkSame(oldFirst4, newFirst4) + val netChanged = + !isNetworkSame(oldFirst6, newFirst6) || !isNetworkSame(oldFirst4, newFirst4) return NetworkChanges(routesChanged, netChanged, mtuChanged) } From bc8ebfdac3fa6944756a89ac9caf4deac602a60c Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:25:48 +0530 Subject: [PATCH 79/81] ktfmt code --- .../celzero/bravedns/adapter/AlertAdapter.kt | 192 +++++++++--------- .../bravedns/adapter/AppConnectionAdapter.kt | 4 - .../adapter/ConnectionTrackerAdapter.kt | 1 - .../bravedns/adapter/CustomIpAdapter.kt | 3 - .../adapter/FirewallAppListAdapter.kt | 2 +- .../bravedns/adapter/OneWgConfigAdapter.kt | 1 - .../RemoteBlocklistCoordinator.kt | 14 +- .../customdownloader/RetrofitManager.kt | 4 +- .../bravedns/scheduler/PaymentWorker.kt | 10 +- .../celzero/bravedns/service/ProxyManager.kt | 4 - .../bravedns/ui/activity/AppInfoActivity.kt | 2 - .../ui/activity/ProxySettingsActivity.kt | 15 +- .../bravedns/ui/activity/WgMainActivity.kt | 4 +- .../ui/fragment/DnsSettingsFragment.kt | 2 +- .../ui/fragment/HomeScreenFragment.kt | 4 +- .../bravedns/ui/fragment/ODoHListFragment.kt | 4 +- .../celzero/bravedns/util/TunnelImporter.kt | 8 +- .../bravedns/viewmodel/DnsLogViewModel.kt | 17 +- .../celzero/bravedns/data/DataUsageSummary.kt | 7 +- .../celzero/bravedns/database/AppInfoDAO.kt | 4 +- .../database/ProxyApplicationMappingDAO.kt | 4 +- .../bravedns/database/RefreshDatabase.kt | 29 +-- .../bravedns/database/WgConfigFilesDAO.kt | 3 +- .../celzero/bravedns/net/go/GoVpnAdapter.kt | 45 ++-- .../bravedns/net/manager/ConnectionTracer.kt | 2 - .../bravedns/service/BraveVPNService.kt | 24 +-- .../bravedns/service/ConnectionMonitor.kt | 6 +- .../celzero/bravedns/service/DnsLogTracker.kt | 6 +- .../celzero/bravedns/service/NetLogTracker.kt | 2 +- .../celzero/bravedns/service/PauseTimer.kt | 4 +- .../bravedns/service/PersistentState.kt | 3 +- .../com/celzero/bravedns/util/KnownPorts.kt | 2 +- .../com/celzero/bravedns/util/OrbotHelper.kt | 4 +- .../bravedns/util/ResourceRecordTypes.kt | 7 +- .../com/celzero/bravedns/util/Utilities.kt | 5 +- 35 files changed, 223 insertions(+), 225 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/AlertAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/AlertAdapter.kt index ce12aa024..855fe5a23 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/AlertAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/AlertAdapter.kt @@ -14,113 +14,113 @@ * limitations under the License. *//* -package com.celzero.bravedns.adapter + package com.celzero.bravedns.adapter -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.celzero.bravedns.database.AlertRegistry -import com.celzero.bravedns.databinding.ListItemAlertRegistryBinding -import com.celzero.bravedns.service.AlertCategory -import com.google.android.material.dialog.MaterialAlertDialogBuilder + import android.content.Context + import android.view.LayoutInflater + import android.view.View + import android.view.ViewGroup + import androidx.recyclerview.widget.RecyclerView + import com.celzero.bravedns.database.AlertRegistry + import com.celzero.bravedns.databinding.ListItemAlertRegistryBinding + import com.celzero.bravedns.service.AlertCategory + import com.google.android.material.dialog.MaterialAlertDialogBuilder -class AlertAdapter( - private val context: Context, - private val alertRegistries: Array -) : RecyclerView.Adapter() { + class AlertAdapter( + private val context: Context, + private val alertRegistries: Array + ) : RecyclerView.Adapter() { - override fun onBindViewHolder(holder: AlertAdapter.AlertRegistryViewHolder, position: Int) { - val alerts: AlertRegistry = alertRegistries[position] ?: return - holder.update(alerts) - } + override fun onBindViewHolder(holder: AlertAdapter.AlertRegistryViewHolder, position: Int) { + val alerts: AlertRegistry = alertRegistries[position] ?: return + holder.update(alerts) + } - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): AlertAdapter.AlertRegistryViewHolder { - val itemBinding = - ListItemAlertRegistryBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return AlertRegistryViewHolder(itemBinding) - } + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): AlertAdapter.AlertRegistryViewHolder { + val itemBinding = + ListItemAlertRegistryBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return AlertRegistryViewHolder(itemBinding) + } - override fun getItemCount(): Int { - return alertRegistries.size - } + override fun getItemCount(): Int { + return alertRegistries.size + } - inner class AlertRegistryViewHolder(private val b: ListItemAlertRegistryBinding) : - RecyclerView.ViewHolder(b.root) { - fun update(alert: AlertRegistry) { - // do not show the alert if the message is empty - if (alert.alertMessage.isEmpty()) { - b.root.visibility = View.GONE - return - } - b.title.text = alert.alertTitle - val message = "" - */ + inner class AlertRegistryViewHolder(private val b: ListItemAlertRegistryBinding) : + RecyclerView.ViewHolder(b.root) { + fun update(alert: AlertRegistry) { + // do not show the alert if the message is empty + if (alert.alertMessage.isEmpty()) { + b.root.visibility = View.GONE + return + } + b.title.text = alert.alertTitle + val message = "" + */ /*when (AlertCategory.valueOf(alert.alertCategory)) { - AlertCategory.DNS -> - "List of domains blocked in past one hour. Click to <>..." - AlertCategory.FIREWALL -> - "List of IP addresses blocked in past one hour. Click to <>..." - AlertCategory.APP -> - "List of apps blocked in past one hour. Click to <>..." - else -> "Unknown category" - }*//* + AlertCategory.DNS -> + "List of domains blocked in past one hour. Click to <>..." + AlertCategory.FIREWALL -> + "List of IP addresses blocked in past one hour. Click to <>..." + AlertCategory.APP -> + "List of apps blocked in past one hour. Click to <>..." + else -> "Unknown category" +}*//* - b.description.text = message - b.descriptionMore.text = alert.alertMessage - b.priority.text = alert.alertSeverity.lowercase().replaceFirstChar(Char::uppercase) - setupClickListeners(alert) - } + b.description.text = message + b.descriptionMore.text = alert.alertMessage + b.priority.text = alert.alertSeverity.lowercase().replaceFirstChar(Char::uppercase) + setupClickListeners(alert) + } - fun setupClickListeners(alert: AlertRegistry) { - b.description.setOnClickListener { - if (b.descriptionMore.visibility == View.VISIBLE) - b.descriptionMore.visibility = View.GONE - else if (b.descriptionMore.visibility == View.GONE) - b.descriptionMore.visibility = View.VISIBLE - } + fun setupClickListeners(alert: AlertRegistry) { + b.description.setOnClickListener { + if (b.descriptionMore.visibility == View.VISIBLE) + b.descriptionMore.visibility = View.GONE + else if (b.descriptionMore.visibility == View.GONE) + b.descriptionMore.visibility = View.VISIBLE + } - b.descriptionMore.setOnClickListener { - if (b.descriptionMore.visibility == View.VISIBLE) - b.descriptionMore.visibility = View.GONE - else if (b.descriptionMore.visibility == View.GONE) - b.descriptionMore.visibility = View.VISIBLE - } + b.descriptionMore.setOnClickListener { + if (b.descriptionMore.visibility == View.VISIBLE) + b.descriptionMore.visibility = View.GONE + else if (b.descriptionMore.visibility == View.GONE) + b.descriptionMore.visibility = View.VISIBLE + } - b.action.setOnClickListener { - val category = AlertCategory.valueOf(alert.alertCategory) - showActionDialog(category) - } - } + b.action.setOnClickListener { + val category = AlertCategory.valueOf(alert.alertCategory) + showActionDialog(category) + } + } - private fun showActionDialog(category: AlertCategory) { - // show dialog with actions to be taken on the alert - val message = "" - */ + private fun showActionDialog(category: AlertCategory) { + // show dialog with actions to be taken on the alert + val message = "" + */ /*when (category) { - AlertCategory.DNS -> - "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow all connections from this domain \n\n 4. Block all connections from this domain" - AlertCategory.FIREWALL -> - "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow this connections for all app \n\n 4. Block this connections for all app" - AlertCategory.APP -> - "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow all connections from this app \n\n 4. Block all connections from this app" - else -> "Unknown category" - }*//* + AlertCategory.DNS -> + "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow all connections from this domain \n\n 4. Block all connections from this domain" + AlertCategory.FIREWALL -> + "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow this connections for all app \n\n 4. Block this connections for all app" + AlertCategory.APP -> + "Some actions to be taken on the alert \n\n 1. Allow the connection \n\n 2. Block the connection \n\n 3. Allow all connections from this app \n\n 4. Block all connections from this app" + else -> "Unknown category" +}*//* - MaterialAlertDialogBuilder(context) - .setTitle("Actions") - .setMessage(message) - .setPositiveButton("Okay") { dialog, _ -> - // allow the connection - dialog.dismiss() - } - .create() - .show() - } - } -} -*/ + MaterialAlertDialogBuilder(context) + .setTitle("Actions") + .setMessage(message) + .setPositiveButton("Okay") { dialog, _ -> + // allow the connection + dialog.dismiss() + } + .create() + .show() + } + } + } + */ diff --git a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt index 8f1d453cd..e1abb1c8e 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/AppConnectionAdapter.kt @@ -24,7 +24,6 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView @@ -36,9 +35,6 @@ import com.celzero.bravedns.ui.bottomsheet.AppConnectionBottomSheet import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.UIUtils.fetchColor import com.celzero.bravedns.util.Utilities.removeBeginningTrailingCommas -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext class AppConnectionAdapter(val context: Context, val lifecycleOwner: LifecycleOwner, val uid: Int) : PagingDataAdapter( diff --git a/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt index 8399428f3..fd63c34fe 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt @@ -34,7 +34,6 @@ import com.bumptech.glide.Glide import com.celzero.bravedns.R import com.celzero.bravedns.database.ConnectionTracker import com.celzero.bravedns.databinding.ConnectionTransactionRowBinding -import com.celzero.bravedns.service.BraveVPNService import com.celzero.bravedns.service.FirewallManager import com.celzero.bravedns.service.FirewallRuleset import com.celzero.bravedns.service.ProxyManager diff --git a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt index f61ce2cb0..c4ae92198 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/CustomIpAdapter.kt @@ -54,9 +54,6 @@ import com.celzero.bravedns.util.Utilities.getFlag import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.dialog.MaterialAlertDialogBuilder -import inet.ipaddr.AddressStringException -import inet.ipaddr.HostName -import inet.ipaddr.HostNameException import inet.ipaddr.IPAddress import inet.ipaddr.IPAddressString import kotlinx.coroutines.Dispatchers diff --git a/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt index 587eaa5be..4133bcdbd 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/FirewallAppListAdapter.kt @@ -43,10 +43,10 @@ import com.celzero.bravedns.ui.activity.AppInfoActivity.Companion.UID_INTENT_NAM import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.util.Utilities.getIcon import com.google.android.material.dialog.MaterialAlertDialogBuilder -import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.util.concurrent.TimeUnit class FirewallAppListAdapter( private val context: Context, diff --git a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt index 2e99c9589..cf10af51a 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/OneWgConfigAdapter.kt @@ -19,7 +19,6 @@ import android.content.Context import android.content.Intent import android.os.SystemClock import android.text.format.DateUtils -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt index 1827fea24..f40d38286 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/RemoteBlocklistCoordinator.kt @@ -28,13 +28,13 @@ import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD import com.celzero.bravedns.util.RemoteFileTagUtil import com.celzero.bravedns.util.Utilities import com.google.gson.JsonObject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import retrofit2.converter.gson.GsonConverterFactory import java.io.File import java.io.IOException import java.util.concurrent.CancellationException import java.util.concurrent.TimeUnit -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import retrofit2.converter.gson.GsonConverterFactory class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { @@ -81,7 +81,8 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam } } } catch (ex: CancellationException) { - Log.e(LOG_TAG_DOWNLOAD, + Log.e( + LOG_TAG_DOWNLOAD, "Local blocklist download, received cancellation exception: ${ex.message}", ex ) @@ -104,7 +105,10 @@ class RemoteBlocklistCoordinator(val context: Context, workerParams: WorkerParam "" ) - Log.i(LOG_TAG_DOWNLOAD, "response rcvd for remote blocklist, res: ${response?.isSuccessful}") + Log.i( + LOG_TAG_DOWNLOAD, + "response rcvd for remote blocklist, res: ${response?.isSuccessful}" + ) if (response?.isSuccessful == true) { return saveRemoteFile(response.body(), timestamp) diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt index 96cbe4745..899facdb6 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/RetrofitManager.kt @@ -18,13 +18,13 @@ package com.celzero.bravedns.customdownloader import android.util.Log import com.celzero.bravedns.util.Constants import com.celzero.bravedns.util.Logger -import java.net.InetAddress -import java.util.concurrent.TimeUnit import okhttp3.Dns import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.dnsoverhttps.DnsOverHttps import retrofit2.Retrofit +import java.net.InetAddress +import java.util.concurrent.TimeUnit class RetrofitManager { diff --git a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt index f71b7dd29..816c318ca 100644 --- a/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt +++ b/app/src/full/java/com/celzero/bravedns/scheduler/PaymentWorker.kt @@ -26,10 +26,10 @@ import com.celzero.bravedns.customdownloader.RetrofitManager import com.celzero.bravedns.service.TcpProxyHelper import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD -import java.util.concurrent.TimeUnit import org.json.JSONObject import org.koin.core.component.KoinComponent import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : CoroutineWorker(context, workerParameters), KoinComponent { @@ -70,7 +70,9 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : } } - private suspend fun getPaymentStatusFromServer(retryCount: Int = 0): TcpProxyHelper.PaymentStatus { + private suspend fun getPaymentStatusFromServer( + retryCount: Int = 0 + ): TcpProxyHelper.PaymentStatus { var paymentStatus = TcpProxyHelper.PaymentStatus.INITIATED try { val retrofit = @@ -115,7 +117,9 @@ class PaymentWorker(val context: Context, workerParameters: WorkerParameters) : e ) } - return if (isRetryRequired(retryCount) && paymentStatus == TcpProxyHelper.PaymentStatus.INITIATED) { + return if ( + isRetryRequired(retryCount) && paymentStatus == TcpProxyHelper.PaymentStatus.INITIATED + ) { Log.i(LOG_TAG_DOWNLOAD, "retrying the payment status check") getPaymentStatusFromServer(retryCount + 1) } else { diff --git a/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt b/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt index 3b6643ff0..d7dbffdbe 100644 --- a/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt @@ -23,9 +23,6 @@ import com.celzero.bravedns.database.AppInfo import com.celzero.bravedns.database.ProxyAppMappingRepository import com.celzero.bravedns.database.ProxyApplicationMapping import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_PROXY -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.util.concurrent.CopyOnWriteArraySet @@ -307,5 +304,4 @@ object ProxyManager : KoinComponent { ipnProxyId != Backend.Block && ipnProxyId != Backend.Exit } - } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt index 1bc75280f..16e48f5a8 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/AppInfoActivity.kt @@ -56,7 +56,6 @@ import com.celzero.bravedns.viewmodel.CustomIpViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject @@ -665,5 +664,4 @@ class AppInfoActivity : AppCompatActivity(R.layout.activity_app_details) { private suspend fun uiCtx(f: suspend () -> Unit) { withContext(Dispatchers.Main) { f() } } - } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt index b2cf2ef39..9b864707b 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/ProxySettingsActivity.kt @@ -486,10 +486,12 @@ class ProxySettingsActivity : AppCompatActivity(R.layout.fragment_proxy_configur } else { wgStatus += getString( - R.string.ci_ip_label, - it.getName(), - getString(R.string.status_waiting).replaceFirstChar(Char::titlecase).padStart(1, ' ') - ) + "\n" + R.string.ci_ip_label, + it.getName(), + getString(R.string.status_waiting) + .replaceFirstChar(Char::titlecase) + .padStart(1, ' ') + ) + "\n" if (DEBUG) Log.d(LOG_TAG_PROXY, "current proxy status is null for $id") } } @@ -686,7 +688,10 @@ class ProxySettingsActivity : AppCompatActivity(R.layout.fragment_proxy_configur } } else { ipAddressEditText.setText(Constants.SOCKS_DEFAULT_IP, TextView.BufferType.EDITABLE) - portEditText.setText(Constants.SOCKS_DEFAULT_PORT.toString(), TextView.BufferType.EDITABLE) + portEditText.setText( + Constants.SOCKS_DEFAULT_PORT.toString(), + TextView.BufferType.EDITABLE + ) } headerTxt.text = getString(R.string.settings_dns_proxy_dialog_header) diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt index 476661b83..95bd0432c 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/WgMainActivity.kt @@ -55,7 +55,8 @@ import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main), OneWgConfigAdapter.DnsStatusListener { +class WgMainActivity : + AppCompatActivity(R.layout.activity_wireguard_main), OneWgConfigAdapter.DnsStatusListener { private val b by viewBinding(ActivityWireguardMainBinding::bind) private val persistentState by inject() private val appConfig by inject() @@ -64,7 +65,6 @@ class WgMainActivity : AppCompatActivity(R.layout.activity_wireguard_main), OneW private var oneWgConfigAdapter: OneWgConfigAdapter? = null private val wgConfigViewModel: WgConfigViewModel by viewModel() - companion object { private const val IMPORT_LAUNCH_INPUT = "*/*" } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt index d712261f3..cf9059c47 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsSettingsFragment.kt @@ -44,11 +44,11 @@ import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.UIUtils.fetchColor import com.celzero.bravedns.util.Utilities import com.celzero.bravedns.util.Utilities.isPlayStoreFlavour -import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.android.ext.android.get import org.koin.android.ext.android.inject +import java.util.concurrent.TimeUnit class DnsSettingsFragment : Fragment(R.layout.fragment_dns_configure), diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt index a6b2772f8..a1f69c1b6 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/HomeScreenFragment.kt @@ -78,14 +78,14 @@ import com.celzero.bravedns.util.Utilities.showToastUiCentered import com.facebook.shimmer.Shimmer import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar -import java.util.* -import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject +import java.util.* +import java.util.concurrent.TimeUnit class HomeScreenFragment : Fragment(R.layout.fragment_home_screen) { private val b by viewBinding(FragmentHomeScreenBinding::bind) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt index f59c4423e..3edeadba0 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/ODoHListFragment.kt @@ -33,14 +33,14 @@ import com.celzero.bravedns.database.ODoHEndpoint import com.celzero.bravedns.databinding.DialogSetCustomOdohBinding import com.celzero.bravedns.databinding.FragmentOdohListBinding import com.celzero.bravedns.viewmodel.ODoHEndpointViewModel -import java.net.MalformedURLException -import java.net.URL import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.get import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel +import java.net.MalformedURLException +import java.net.URL class ODoHListFragment : Fragment(R.layout.fragment_odoh_list) { private val b by viewBinding(FragmentOdohListBinding::bind) diff --git a/app/src/full/java/com/celzero/bravedns/util/TunnelImporter.kt b/app/src/full/java/com/celzero/bravedns/util/TunnelImporter.kt index b6f6b5432..a8f350891 100644 --- a/app/src/full/java/com/celzero/bravedns/util/TunnelImporter.kt +++ b/app/src/full/java/com/celzero/bravedns/util/TunnelImporter.kt @@ -28,16 +28,16 @@ import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.service.WireguardManager import com.celzero.bravedns.wireguard.Config import com.celzero.bravedns.wireguard.util.ErrorMessages +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.BufferedReader import java.io.ByteArrayInputStream import java.io.InputStreamReader import java.nio.charset.StandardCharsets import java.util.zip.ZipEntry import java.util.zip.ZipInputStream -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject object TunnelImporter : KoinComponent { diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt index 394be128b..06f8cf92a 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/DnsLogViewModel.kt @@ -39,14 +39,15 @@ class DnsLogViewModel(private val dnsLogDAO: DnsLogDAO) : ViewModel() { init { filteredList.value = "" - pagingConfig = PagingConfig( - enablePlaceholders = true, - prefetchDistance = 3, - initialLoadSize = LIVEDATA_PAGE_SIZE * 2, - maxSize = LIVEDATA_PAGE_SIZE * 3, - pageSize = LIVEDATA_PAGE_SIZE * 2, - jumpThreshold = 5 - ) + pagingConfig = + PagingConfig( + enablePlaceholders = true, + prefetchDistance = 3, + initialLoadSize = LIVEDATA_PAGE_SIZE * 2, + maxSize = LIVEDATA_PAGE_SIZE * 3, + pageSize = LIVEDATA_PAGE_SIZE * 2, + jumpThreshold = 5 + ) } val dnsLogsList = filteredList.switchMap { input -> fetchDnsLogs(input) } diff --git a/app/src/main/java/com/celzero/bravedns/data/DataUsageSummary.kt b/app/src/main/java/com/celzero/bravedns/data/DataUsageSummary.kt index fcbbc3b57..901168c1d 100644 --- a/app/src/main/java/com/celzero/bravedns/data/DataUsageSummary.kt +++ b/app/src/main/java/com/celzero/bravedns/data/DataUsageSummary.kt @@ -15,4 +15,9 @@ */ package com.celzero.bravedns.data -data class DataUsageSummary(val totalDownload: Long, val totalUpload: Long, val connectionsCount: Int, val meteredDataUsage: Long) +data class DataUsageSummary( + val totalDownload: Long, + val totalUpload: Long, + val connectionsCount: Int, + val meteredDataUsage: Long +) diff --git a/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt b/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt index bfa0f8040..16f7f7489 100644 --- a/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/AppInfoDAO.kt @@ -24,7 +24,6 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import com.celzero.bravedns.data.DataUsage -import com.celzero.bravedns.util.Constants.Companion.RETHINK_PACKAGE @Dao interface AppInfoDAO { @@ -32,7 +31,8 @@ interface AppInfoDAO { @Update fun update(appInfo: AppInfo): Int @Query( - "update AppInfo set firewallStatus = :firewallStatus, connectionStatus = :connectionStatus where uid = :uid and packageName != 'com.celzero.bravedns'") + "update AppInfo set firewallStatus = :firewallStatus, connectionStatus = :connectionStatus where uid = :uid and packageName != 'com.celzero.bravedns'" + ) fun updateFirewallStatusByUid(uid: Int, firewallStatus: Int, connectionStatus: Int) @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(appInfo: AppInfo): Long diff --git a/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt b/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt index 6ddf39248..cba41cc96 100644 --- a/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/ProxyApplicationMappingDAO.kt @@ -79,9 +79,7 @@ interface ProxyApplicationMappingDAO { @Query("update ProxyApplicationMapping set proxyId = :cfgId, proxyName = :cfgName") fun updateProxyForAllApps(cfgId: String, cfgName: String = "") - @Query( - "update ProxyApplicationMapping set proxyName = :proxyName where proxyId = :proxyId" - ) + @Query("update ProxyApplicationMapping set proxyName = :proxyName where proxyId = :proxyId") fun updateProxyNameForProxyId(proxyId: String, proxyName: String) @Query( diff --git a/app/src/main/java/com/celzero/bravedns/database/RefreshDatabase.kt b/app/src/main/java/com/celzero/bravedns/database/RefreshDatabase.kt index bcfbb94bd..f035820a1 100644 --- a/app/src/main/java/com/celzero/bravedns/database/RefreshDatabase.kt +++ b/app/src/main/java/com/celzero/bravedns/database/RefreshDatabase.kt @@ -510,8 +510,7 @@ internal constructor( var builder: NotificationCompat.Builder if (isAtleastO()) { val name: CharSequence = ctx.getString(R.string.notif_channel_firewall_alerts) - val description = - ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) + val description = ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(NOTIF_CHANNEL_ID_FIREWALL_ALERTS, name, importance) channel.description = description @@ -521,13 +520,9 @@ internal constructor( builder = NotificationCompat.Builder(ctx, NOTIF_CHANNEL_ID_FIREWALL_ALERTS) } - val contentTitle: String = - ctx.resources.getString(R.string.new_app_bulk_notification_title) + val contentTitle: String = ctx.resources.getString(R.string.new_app_bulk_notification_title) val contentText: String = - ctx.resources.getString( - R.string.new_app_bulk_notification_content, - appSize.toString() - ) + ctx.resources.getString(R.string.new_app_bulk_notification_content, appSize.toString()) builder .setSmallIcon(R.drawable.ic_notification_icon) @@ -536,8 +531,7 @@ internal constructor( .setContentText(contentText) builder.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) - builder.color = - ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) + builder.color = ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) // Secret notifications are not shown on the lock screen. No need for this app to show // there. @@ -594,8 +588,7 @@ internal constructor( val builder: NotificationCompat.Builder if (isAtleastO()) { val name: CharSequence = ctx.getString(R.string.notif_channel_firewall_alerts) - val description = - ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) + val description = ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(NOTIF_CHANNEL_ID_FIREWALL_ALERTS, name, importance) channel.description = description @@ -616,8 +609,7 @@ internal constructor( .setContentText(contentText) builder.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) - builder.color = - ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) + builder.color = ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) val openIntent1 = makeNewAppVpnIntent( @@ -668,8 +660,7 @@ internal constructor( ) if (isAtleastO()) { val name: CharSequence = ctx.getString(R.string.notif_channel_firewall_alerts) - val description = - ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) + val description = ctx.resources.getString(R.string.notif_channel_desc_firewall_alerts) val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(NOTIF_CHANNEL_ID_FIREWALL_ALERTS, name, importance) channel.description = description @@ -686,8 +677,7 @@ internal constructor( .setContentIntent(pendingIntent) .setContentText(contentText) builder.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) - builder.color = - ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) + builder.color = ContextCompat.getColor(ctx, UIUtils.getAccentColor(persistentState.theme)) val openIntent = makeVpnIntent(NOTIF_ID_LOAD_RULES_FAIL, Constants.NOTIF_ACTION_RULES_FAILURE) val notificationAction: NotificationCompat.Action = @@ -774,8 +764,7 @@ internal constructor( @RequiresApi(Build.VERSION_CODES.O) private fun appInfoCategory(ai: ApplicationInfo): String { val cat = ApplicationInfo.getCategoryTitle(ctx, ai.category) - return cat?.toString() - ?: ctx.getString(FirewallManager.CategoryConstants.OTHER.nameResId) + return cat?.toString() ?: ctx.getString(FirewallManager.CategoryConstants.OTHER.nameResId) } private fun replaceUnderscore(s: String): String { diff --git a/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt b/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt index baaec1eac..5d07bf095 100644 --- a/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt +++ b/app/src/main/java/com/celzero/bravedns/database/WgConfigFilesDAO.kt @@ -69,5 +69,6 @@ interface WgConfigFilesDAO { @Query("select count(id) from WgConfigFiles where id != $SEC_WARP_ID and id != $WARP_ID") fun getConfigCount(): LiveData - @Query("update WgConfigFiles set isActive = 0, oneWireGuard = 0 where id = :id") fun disableConfig(id: Int) + @Query("update WgConfigFiles set isActive = 0, oneWireGuard = 0 where id = :id") + fun disableConfig(id: Int) } diff --git a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt index 15210f455..87dd68084 100644 --- a/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt +++ b/app/src/main/java/com/celzero/bravedns/net/go/GoVpnAdapter.kt @@ -52,12 +52,12 @@ import com.celzero.bravedns.util.Utilities.showToastUiCentered import com.celzero.bravedns.wireguard.Config import intra.Intra import intra.Tunnel -import java.net.URI import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import java.net.URI /** * This is a VpnAdapter that captures all traffic and routes it through a go-tun2socks instance with @@ -872,7 +872,11 @@ class GoVpnAdapter : KoinComponent { } } - suspend fun updateLinkAndRoutes(tunFd: ParcelFileDescriptor, opts: TunnelOptions, proto: Long): Boolean { + suspend fun updateLinkAndRoutes( + tunFd: ParcelFileDescriptor, + opts: TunnelOptions, + proto: Long + ): Boolean { if (!tunnel.isConnected) { Log.e(LOG_TAG_VPN, "updateLink: tunFd is null, returning") return false @@ -1006,11 +1010,12 @@ class GoVpnAdapter : KoinComponent { } fun syncP50Latency(id: String) { - val tid: String = if (persistentState.enableDnsCache && !id.startsWith(Backend.CT)) { - Backend.CT + id - } else { - id - } + val tid: String = + if (persistentState.enableDnsCache && !id.startsWith(Backend.CT)) { + Backend.CT + id + } else { + id + } try { val transport = getResolver()?.get(tid) val p50 = transport?.p50() ?: return @@ -1089,18 +1094,22 @@ class GoVpnAdapter : KoinComponent { val router = tunnel.proxies.getProxy(proxyId).router() // if the router contains 0.0.0.0, then it is not split tunnel for ipv4 // if the router contains ::, then it is not split tunnel for ipv6 - val res: Boolean = if (pair.first && pair.second) { - // if the pair is true, check for both ipv4 and ipv6 - !router.contains(UNSPECIFIED_IP_IPV4) || !router.contains(UNSPECIFIED_IP_IPV6) - } else if (pair.first) { - !router.contains(UNSPECIFIED_IP_IPV4) - } else if (pair.second) { - !router.contains(UNSPECIFIED_IP_IPV6) - } else { - false - } + val res: Boolean = + if (pair.first && pair.second) { + // if the pair is true, check for both ipv4 and ipv6 + !router.contains(UNSPECIFIED_IP_IPV4) || !router.contains(UNSPECIFIED_IP_IPV6) + } else if (pair.first) { + !router.contains(UNSPECIFIED_IP_IPV4) + } else if (pair.second) { + !router.contains(UNSPECIFIED_IP_IPV6) + } else { + false + } - Log.i(LOG_TAG_VPN, "split tunnel proxy($proxyId): ipv4? ${pair.first}, ipv6? ${pair.second}, res? $res") + Log.i( + LOG_TAG_VPN, + "split tunnel proxy($proxyId): ipv4? ${pair.first}, ipv6? ${pair.second}, res? $res" + ) res } catch (e: Exception) { Log.w(LOG_TAG_VPN, "err isSplitTunnelProxy($proxyId): ${e.message}") diff --git a/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt b/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt index 17433411b..f79272556 100644 --- a/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt +++ b/app/src/main/java/com/celzero/bravedns/net/manager/ConnectionTracer.kt @@ -14,8 +14,6 @@ import com.celzero.bravedns.util.Utilities.isUnspecifiedIp import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import inet.ipaddr.IPAddressString -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import java.net.InetSocketAddress import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt index 60fdfc737..e4230b45f 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -92,18 +92,6 @@ import inet.ipaddr.HostName import inet.ipaddr.IPAddressString import intra.Bridge import intra.SocketSummary -import java.io.IOException -import java.net.InetAddress -import java.net.SocketException -import java.net.UnknownHostException -import java.util.Collections -import java.util.Locale -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.math.abs -import kotlin.math.min -import kotlin.random.Random import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -116,6 +104,18 @@ import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import rnet.ServerSummary import rnet.Tab +import java.io.IOException +import java.net.InetAddress +import java.net.SocketException +import java.net.UnknownHostException +import java.util.Collections +import java.util.Locale +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.abs +import kotlin.math.min +import kotlin.random.Random class BraveVPNService : VpnService(), ConnectionMonitor.NetworkListener, Bridge, OnSharedPreferenceChangeListener { diff --git a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt index 095b8d17a..af238780f 100644 --- a/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt +++ b/app/src/main/java/com/celzero/bravedns/service/ConnectionMonitor.kt @@ -37,6 +37,9 @@ import com.celzero.bravedns.util.Utilities.isAtleastQ import com.celzero.bravedns.util.Utilities.isAtleastS import com.celzero.bravedns.util.Utilities.isNetworkSame import com.google.common.collect.Sets +import kotlinx.coroutines.runBlocking +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.Closeable import java.io.IOException import java.net.DatagramSocket @@ -49,9 +52,6 @@ import java.util.Collections import java.util.concurrent.TimeUnit import kotlin.math.max import kotlin.math.min -import kotlinx.coroutines.runBlocking -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject class ConnectionMonitor(context: Context, networkListener: NetworkListener) : ConnectivityManager.NetworkCallback(), KoinComponent { diff --git a/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt b/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt index 77ffe5258..1259d49e4 100644 --- a/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/DnsLogTracker.kt @@ -148,9 +148,11 @@ internal constructor( if (transaction.response.isNotEmpty()) { dnsLog.response = transaction.response.take(RDATA_MAX_LENGTH) } - // now, there is no empty response, instead -- is added as response from go + // there is no empty response, instead -- is added as response from go, + // there are cases where the response so check for empty response as well if ( - transaction.response == EMPTY_RESPONSE && + (transaction.response.isEmpty() || + transaction.response == EMPTY_RESPONSE) && (transaction.blocklist.isNotEmpty() || transaction.upstreamBlock) ) { dnsLog.isBlocked = true diff --git a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt index 197892061..ac6e27fdc 100644 --- a/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/NetLogTracker.kt @@ -28,13 +28,13 @@ import com.celzero.bravedns.database.DnsLogRepository import com.celzero.bravedns.database.RethinkLog import com.celzero.bravedns.database.RethinkLogRepository import com.celzero.bravedns.util.NetLogBatcher -import java.util.Calendar import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject +import java.util.Calendar class NetLogTracker internal constructor( diff --git a/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt b/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt index 208dcbb55..37b284942 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PauseTimer.kt @@ -86,7 +86,5 @@ object PauseTimer { return pauseCountDownTimer } - private fun io(f: suspend () -> Unit) = CoroutineScope(Dispatchers.IO).launch { - f() - } + private fun io(f: suspend () -> Unit) = CoroutineScope(Dispatchers.IO).launch { f() } } diff --git a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt index 1ff9a6bea..e4fd264bd 100644 --- a/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt +++ b/app/src/main/java/com/celzero/bravedns/service/PersistentState.kt @@ -271,7 +271,8 @@ class PersistentState(context: Context) : SimpleKrate(context), KoinComponent { var routeRethinkInRethink by booleanPref("route_rethink_in_rethink").withDefault(false) // perform connectivity checks - var connectivityChecks by booleanPref("connectivity_check").withDefault(!Utilities.isPlayStoreFlavour()) + var connectivityChecks by + booleanPref("connectivity_check").withDefault(Utilities.isPlayStoreFlavour()) // proxy dns requests over proxy var proxyDns by booleanPref("proxy_dns").withDefault(true) diff --git a/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt b/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt index cb748414b..31a53fe5f 100644 --- a/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt +++ b/app/src/main/java/com/celzero/bravedns/util/KnownPorts.kt @@ -39,7 +39,7 @@ class KnownPorts { return port == NTP_PORT } - fun isDns(port: Int): Boolean { + fun isDns(port: Int?): Boolean { return port == DNS_PORT } diff --git a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt index 51d6ed17e..2f37b1fb4 100644 --- a/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt +++ b/app/src/main/java/com/celzero/bravedns/util/OrbotHelper.kt @@ -50,13 +50,13 @@ import com.celzero.bravedns.util.Utilities.isAtleastT import com.celzero.bravedns.util.Utilities.isFdroidFlavour import com.celzero.bravedns.util.Utilities.isPlayStoreFlavour import com.celzero.bravedns.util.Utilities.isValidPort -import java.net.URI -import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.net.URI +import java.util.concurrent.TimeUnit /** * One-click Orbot setup. diff --git a/app/src/main/java/com/celzero/bravedns/util/ResourceRecordTypes.kt b/app/src/main/java/com/celzero/bravedns/util/ResourceRecordTypes.kt index a9af15ad1..aba66b241 100644 --- a/app/src/main/java/com/celzero/bravedns/util/ResourceRecordTypes.kt +++ b/app/src/main/java/com/celzero/bravedns/util/ResourceRecordTypes.kt @@ -123,9 +123,10 @@ enum class ResourceRecordTypes(val value: Int, val desc: String) { } fun getHandledTypes(): Set { - val list = enumValues().filter { - it == A || it == AAAA || it == CNAME || it == HTTPS || it == SVCB - } + val list = + enumValues().filter { + it == A || it == AAAA || it == CNAME || it == HTTPS || it == SVCB + } return list.map { it.desc }.toSet() } } diff --git a/app/src/main/java/com/celzero/bravedns/util/Utilities.kt b/app/src/main/java/com/celzero/bravedns/util/Utilities.kt index c330bc92d..a52b68262 100644 --- a/app/src/main/java/com/celzero/bravedns/util/Utilities.kt +++ b/app/src/main/java/com/celzero/bravedns/util/Utilities.kt @@ -325,10 +325,7 @@ object Utilities { PackageManager.PackageInfoFlags.of(PackageManager.GET_META_DATA.toLong()) ) } else { - ctx.packageManager.getPackageInfo( - ctx.packageName, - PackageManager.GET_META_DATA - ) + ctx.packageManager.getPackageInfo(ctx.packageName, PackageManager.GET_META_DATA) } ) { return firstInstallTime == lastUpdateTime From 9d305cf001eff7f587a4783bd08a57dc984eeb58 Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:26:17 +0530 Subject: [PATCH 80/81] ktfmt code --- .../bravedns/adapter/DnsQueryAdapter.kt | 1 - .../bravedns/adapter/WgConfigAdapter.kt | 2 +- .../customdownloader/IBlocklistDownload.kt | 5 ++++- .../LocalBlocklistCoordinator.kt | 6 +++--- .../download/BlocklistDownloadHelper.kt | 3 +-- .../celzero/bravedns/service/TcpProxyHelper.kt | 6 +++--- .../bravedns/service/WireguardManager.kt | 18 ++++++++++-------- .../bravedns/ui/activity/PauseActivity.kt | 6 +++++- .../ui/bottomsheet/ConnTrackerBottomSheet.kt | 4 +++- .../ui/bottomsheet/DnsBlocklistBottomSheet.kt | 6 +++--- .../ui/fragment/DnsCryptListFragment.kt | 3 +-- .../ui/fragment/RethinkBlocklistFragment.kt | 2 +- .../viewmodel/SummaryStatisticsViewModel.kt | 1 - 13 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt index 9e49036a5..4ce3693be 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/DnsQueryAdapter.kt @@ -44,7 +44,6 @@ import com.celzero.bravedns.ui.bottomsheet.DnsBlocklistBottomSheet import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DNS_LOG import com.celzero.bravedns.util.UIUtils.fetchColor -import com.celzero.bravedns.util.Utilities import com.google.gson.Gson class DnsQueryAdapter(val context: Context, val loadFavIcon: Boolean) : diff --git a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt index a196c9aa4..6e740f090 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/WgConfigAdapter.kt @@ -40,11 +40,11 @@ import com.celzero.bravedns.ui.activity.WgConfigDetailActivity import com.celzero.bravedns.ui.activity.WgConfigEditorActivity.Companion.INTENT_EXTRA_WG_ID import com.celzero.bravedns.util.UIUtils import com.celzero.bravedns.util.Utilities -import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.util.concurrent.ConcurrentHashMap class WgConfigAdapter(private val context: Context) : PagingDataAdapter(DIFF_CALLBACK) { diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/IBlocklistDownload.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/IBlocklistDownload.kt index 498ba0c42..87c39c405 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/IBlocklistDownload.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/IBlocklistDownload.kt @@ -18,7 +18,10 @@ package com.celzero.bravedns.customdownloader import com.google.gson.JsonObject import okhttp3.ResponseBody import retrofit2.Response -import retrofit2.http.* +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query +import retrofit2.http.Streaming interface IBlocklistDownload { diff --git a/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt b/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt index 5102e7c95..0df103ddb 100644 --- a/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt +++ b/app/src/full/java/com/celzero/bravedns/customdownloader/LocalBlocklistCoordinator.kt @@ -45,6 +45,9 @@ import com.celzero.bravedns.util.Utilities.blocklistDownloadBasePath import com.celzero.bravedns.util.Utilities.calculateMd5 import com.celzero.bravedns.util.Utilities.getTagValueFromJson import com.celzero.bravedns.util.Utilities.tempDownloadBasePath +import okhttp3.ResponseBody +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream @@ -53,9 +56,6 @@ import java.io.InputStream import java.io.OutputStream import java.util.concurrent.CancellationException import java.util.concurrent.TimeUnit -import okhttp3.ResponseBody -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject class LocalBlocklistCoordinator(val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { diff --git a/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt b/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt index 77e9a11a0..861b9d619 100644 --- a/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt +++ b/app/src/full/java/com/celzero/bravedns/download/BlocklistDownloadHelper.kt @@ -26,11 +26,10 @@ import com.celzero.bravedns.util.Constants.Companion.INIT_TIME_MS import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_DOWNLOAD import com.celzero.bravedns.util.Utilities.blocklistCanonicalPath import com.celzero.bravedns.util.Utilities.deleteRecursive -import java.io.File -import java.lang.RuntimeException import org.json.JSONException import org.json.JSONObject import retrofit2.converter.gson.GsonConverterFactory +import java.io.File class BlocklistDownloadHelper { diff --git a/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt b/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt index 57ea852a9..541ffe0b6 100644 --- a/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt +++ b/app/src/full/java/com/celzero/bravedns/service/TcpProxyHelper.kt @@ -19,9 +19,6 @@ import com.celzero.bravedns.database.TcpProxyRepository import com.celzero.bravedns.scheduler.PaymentWorker import com.celzero.bravedns.util.Logger import com.celzero.bravedns.util.Logger.Companion.LOG_TAG_PROXY -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -29,6 +26,9 @@ import org.json.JSONObject import org.koin.core.component.KoinComponent import org.koin.core.component.inject import retrofit2.converter.gson.GsonConverterFactory +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit object TcpProxyHelper : KoinComponent { diff --git a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt index 75fa346d7..2c67b43f2 100644 --- a/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/WireguardManager.kt @@ -16,7 +16,6 @@ package com.celzero.bravedns.service import android.content.Context -import android.os.SystemClock import android.util.Log import backend.Backend import backend.WgKey @@ -34,12 +33,6 @@ import com.celzero.bravedns.wireguard.Config import com.celzero.bravedns.wireguard.Peer import com.celzero.bravedns.wireguard.WgInterface import inet.ipaddr.IPAddressString -import java.io.ByteArrayInputStream -import java.io.File -import java.io.InputStream -import java.nio.charset.StandardCharsets -import java.util.Locale -import java.util.concurrent.CopyOnWriteArraySet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -47,6 +40,12 @@ import org.json.JSONObject import org.koin.core.component.KoinComponent import org.koin.core.component.inject import retrofit2.converter.gson.GsonConverterFactory +import java.io.ByteArrayInputStream +import java.io.File +import java.io.InputStream +import java.nio.charset.StandardCharsets +import java.util.Locale +import java.util.concurrent.CopyOnWriteArraySet object WireguardManager : KoinComponent { @@ -247,7 +246,10 @@ object WireguardManager : KoinComponent { fun disableConfig(unmapped: WgConfigFiles) { val map = mappings.find { it.id == unmapped.id } if (map == null) { - Log.e(LOG_TAG_PROXY, "disableConfig: wg not found, id: ${unmapped.id}, ${mappings.size}") + Log.e( + LOG_TAG_PROXY, + "disableConfig: wg not found, id: ${unmapped.id}, ${mappings.size}" + ) return } diff --git a/app/src/full/java/com/celzero/bravedns/ui/activity/PauseActivity.kt b/app/src/full/java/com/celzero/bravedns/ui/activity/PauseActivity.kt index c58812337..27c3eb091 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/activity/PauseActivity.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/activity/PauseActivity.kt @@ -35,7 +35,11 @@ import com.celzero.bravedns.service.VpnController import com.celzero.bravedns.ui.HomeScreenActivity import com.celzero.bravedns.util.Constants.Companion.INIT_TIME_MS import com.celzero.bravedns.util.Themes -import kotlinx.coroutines.* +import kotlinx.coroutines.CompletableJob +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import java.util.concurrent.TimeUnit diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt index 615da287f..7cf3f4b5a 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/ConnTrackerBottomSheet.kt @@ -143,7 +143,9 @@ class ConnTrackerBottomSheet : BottomSheetDialogFragment(), KoinComponent { // setup click and item selected listeners setupClickListeners() - val rethinkUid = Utilities.getApplicationInfo(requireContext(), requireContext().packageName)?.uid ?: Constants.INVALID_UID + val rethinkUid = + Utilities.getApplicationInfo(requireContext(), requireContext().packageName)?.uid + ?: Constants.INVALID_UID if (info!!.uid == rethinkUid) { // do not show rules for RethinkDNS app b.bsConnBlockedRule1HeaderLl.visibility = View.GONE diff --git a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt index b17fbef67..884c290df 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/bottomsheet/DnsBlocklistBottomSheet.kt @@ -42,7 +42,6 @@ import com.bumptech.glide.request.transition.Transition import com.celzero.bravedns.R import com.celzero.bravedns.RethinkDnsApplication.Companion.DEBUG import com.celzero.bravedns.adapter.FirewallStatusSpinnerAdapter -import com.celzero.bravedns.data.AppConfig import com.celzero.bravedns.database.DnsLog import com.celzero.bravedns.databinding.BottomSheetDnsLogBinding import com.celzero.bravedns.databinding.DialogInfoRulesLayoutBinding @@ -62,10 +61,10 @@ import com.google.android.material.chip.Chip import com.google.common.collect.HashMultimap import com.google.common.collect.Multimap import com.google.gson.Gson -import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.android.ext.android.inject +import java.util.Locale class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { private var _binding: BottomSheetDnsLogBinding? = null @@ -451,7 +450,8 @@ class DnsBlocklistBottomSheet : BottomSheetDialogFragment() { } if (log!!.isAnonymized()) { // anonymized queries answered by dns-crypt - val text = getString(R.string.dns_btm_resolved_crypt, uptime, log!!.serverIP, log!!.relayIP) + val text = + getString(R.string.dns_btm_resolved_crypt, uptime, log!!.serverIP, log!!.relayIP) b.dnsBlockBlockedDesc.text = updateHtmlEncodedText(text) } else if (log!!.isLocallyAnswered()) { // usually happens when there is a network failure b.dnsBlockBlockedDesc.text = getString(R.string.dns_btm_resolved_doh_no_server, uptime) diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt index b4ecc4878..a805b92aa 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/DnsCryptListFragment.kt @@ -81,8 +81,7 @@ class DnsCryptListFragment : Fragment(R.layout.fragment_dns_crypt_list) { dnsCryptLayoutManager = LinearLayoutManager(requireContext()) b.recyclerDnsCryptConnections.layoutManager = dnsCryptLayoutManager - dnsCryptRecyclerAdapter = - DnsCryptEndpointAdapter(requireContext(), get()) + dnsCryptRecyclerAdapter = DnsCryptEndpointAdapter(requireContext(), get()) dnsCryptViewModel.dnsCryptEndpointList.observe(viewLifecycleOwner) { dnsCryptRecyclerAdapter.submitData(viewLifecycleOwner.lifecycle, it) } diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt index c954ff70e..98811e10d 100644 --- a/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt +++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/RethinkBlocklistFragment.kt @@ -74,12 +74,12 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder -import java.util.regex.Pattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel +import java.util.regex.Pattern class RethinkBlocklistFragment : Fragment(R.layout.fragment_rethink_blocklist), SearchView.OnQueryTextListener { diff --git a/app/src/full/java/com/celzero/bravedns/viewmodel/SummaryStatisticsViewModel.kt b/app/src/full/java/com/celzero/bravedns/viewmodel/SummaryStatisticsViewModel.kt index f792234ee..da01137f9 100644 --- a/app/src/full/java/com/celzero/bravedns/viewmodel/SummaryStatisticsViewModel.kt +++ b/app/src/full/java/com/celzero/bravedns/viewmodel/SummaryStatisticsViewModel.kt @@ -28,7 +28,6 @@ import com.celzero.bravedns.data.DataUsageSummary import com.celzero.bravedns.database.ConnectionTracker import com.celzero.bravedns.database.ConnectionTrackerDAO import com.celzero.bravedns.database.DnsLogDAO -import com.celzero.bravedns.ui.fragment.SummaryStatisticsFragment import com.celzero.bravedns.util.Constants class SummaryStatisticsViewModel( From 0c9e7382f7528d892ea3249a72fa96e42d921b1f Mon Sep 17 00:00:00 2001 From: hussainmohd-a Date: Tue, 2 Apr 2024 00:26:40 +0530 Subject: [PATCH 81/81] v055d string literals --- app/src/main/res/values/strings.xml | 44 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f0061a57e..8089e7a25 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,10 +90,13 @@ Starting Overall Stopped + Idle private secure anonymous Peer + Split + on-device %1$s 🔺 %1$s 🔻 @@ -114,6 +117,8 @@ / \u25be \u25b4 + 🔒 + ⚡ 1 24 @@ -166,6 +171,10 @@ Protected! Connected to DNS + Firewall. + + Protected! Connected to DNS + Firewall + Proxy. + + Paused! Blocked apps will continue to be firewalled. @@ -387,20 +396,15 @@ Choose mode - Chirayu Desai

- 1. New feature: Support for anonymizing Oblivious DNS-over-HTTPS.
- 2. New feature: Support for DNS-over-TLS.
- 3. New feature: Simple and Advanced WireGuard modes.
- 4. New feature: Show bandwidth in Stats screen.
- 5. New feature: Support all proxies including Orbot even in VPN Lockdown mode.
- 6. New feature: Monitor Rethink\'s network traffic using Rethink!
- 7. New feature: Forward DNS over active proxies like WireGuard.
- 8. New feature: Same version backup & restore across devices.
- 9. Improved bug report generation.
- 10. Better handling of dual-stack IP networks.
- 11. Bug fix: Minor bug fixes in handling of multicast DNS.
- 12. Bug fix: Auto-recover connectivity for WireGuard VPNs.
- 13. Bug fix: Fix incorrectly blocked app notifications.

+ Chirayu Desai 3.0

+ 1. New feature: Optionally proxy DNS over WireGuard and SOCKS5 proxies.
+ 2. New feature: Optionally enable built-in Android connectivity checks.
+ 3. Improved support for editing IP-based firewall rules.
+ 4. Improved WireGuard bandwidth.
+ 5. Overhauled WireGuard UX.
+ 6. Avoid connection leaks for Simple and Always-on WireGuard modes.
+ 7. Fix crash when editing WireGuard configurations.
+ 8. Fix minor bugs with RDNS+ and other domain-based firewall rules.

Help translate this app]]>
@@ -417,6 +421,9 @@ Force IPv4 egress Translate outbound IPv6 traffic to IPv4 on-device. Enable if you are connected to an IPv4-only network. + Perform connectivity checks + Connectivity checks are sent to ascertain reachability. + Do not route Private IPs (experimental) Exclude LAN, loopback, multicast, link-local routes from Rethink\'s VPN tunnel. @@ -570,7 +577,7 @@ Choose IP version IPv4 (default) IPv6 (experimental) - Auto (experimental)\nconnectivity checks are sent to ascertain reachability. + Auto (experimental) IPv4 IPv6 Auto @@ -712,6 +719,9 @@ Prompt on blocklist updates Check for blocklist updates once every three days + Never proxy DNS + Do not proxy DNS over Always-on WireGuard, SOCKS5, HTTP proxies. + Rethink is the easiest way to monitor app activity, circumvent Internet censorship, and firewall apps on your Android device. Rethink is a free and open-source project, led by ex-engineers from Amazon, IBM, and Scientific Games. @@ -1062,6 +1072,8 @@ coming soon + %1$s is disabled as proxies are active + paused Note: %1$s blocked apps will continue to be firewalled. @@ -1225,7 +1237,7 @@ Trust delete %1$s %2$s - %1$s:%2$s + %1$s: %2$s No such app