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 4c93c361d..d4623f4b6 100644 --- a/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt +++ b/app/src/full/java/com/celzero/bravedns/adapter/ConnectionTrackerAdapter.kt @@ -34,14 +34,15 @@ import com.celzero.bravedns.database.ConnectionTracker import com.celzero.bravedns.databinding.ConnectionTransactionRowBinding import com.celzero.bravedns.service.FirewallManager import com.celzero.bravedns.service.FirewallRuleset +import com.celzero.bravedns.service.ProxyManager import com.celzero.bravedns.service.VpnController import com.celzero.bravedns.ui.bottomsheet.ConnTrackerBottomSheet import com.celzero.bravedns.util.Constants.Companion.TIME_FORMAT_1 import com.celzero.bravedns.util.KnownPorts import com.celzero.bravedns.util.LoggerConstants.Companion.LOG_TAG_UI import com.celzero.bravedns.util.Protocol +import com.celzero.bravedns.util.UIUtils.getDurationInHumanReadableFormat import com.celzero.bravedns.util.Utilities -import com.celzero.bravedns.util.Utilities.getDurationInHumanReadableFormat import com.celzero.bravedns.util.Utilities.getIcon import com.google.gson.Gson import java.util.Locale @@ -115,10 +116,7 @@ class ConnectionTrackerAdapter(private val context: Context) : val bottomSheetFragment = ConnTrackerBottomSheet() // see AppIpRulesAdapter.kt#openBottomSheet() val bundle = Bundle() - bundle.putString( - ConnTrackerBottomSheet.INSTANCE_STATE_IPDETAILS, - Gson().toJson(ct) - ) + bundle.putString(ConnTrackerBottomSheet.INSTANCE_STATE_IPDETAILS, Gson().toJson(ct)) bottomSheetFragment.arguments = bundle bottomSheetFragment.show(context.supportFragmentManager, bottomSheetFragment.tag) } @@ -220,7 +218,7 @@ class ConnectionTrackerAdapter(private val context: Context) : b.connectionDelay.text = "" hasMinSummary = true } - if (isConnectionProxied(ct.blockedByRule)) { + if (isConnectionProxied(ct.blockedByRule, ct.proxyDetails)) { b.connectionSummaryLl.visibility = View.VISIBLE b.connectionDelay.text = context.getString(R.string.symbol_key) hasMinSummary = true @@ -254,7 +252,7 @@ class ConnectionTrackerAdapter(private val context: Context) : b.connectionDelay.text = b.connectionDelay.text.toString() + context.getString(R.string.symbol_turtle) } - if (isConnectionProxied(ct.blockedByRule)) { + if (isConnectionProxied(ct.blockedByRule, ct.proxyDetails)) { b.connectionDelay.text = b.connectionDelay.text.toString() + context.getString(R.string.symbol_key) } @@ -263,12 +261,11 @@ class ConnectionTrackerAdapter(private val context: Context) : } } - private fun isConnectionProxied(ruleName: String?): Boolean { + private fun isConnectionProxied(ruleName: String?, proxyDetails: String): Boolean { if (ruleName == null) return false - val rule = FirewallRuleset.getFirewallRule(ruleName) ?: return false - - return FirewallRuleset.isProxied(rule) + val proxy = ProxyManager.isProxied(proxyDetails) + return FirewallRuleset.isProxied(rule) || proxy } private fun isConnectionHeavier(ct: ConnectionTracker): Boolean { 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 e00226e92..98ce86e4d 100644 --- a/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt +++ b/app/src/full/java/com/celzero/bravedns/service/ProxyManager.kt @@ -22,6 +22,7 @@ import com.celzero.bravedns.database.AppInfo import com.celzero.bravedns.database.ProxyAppMappingRepository import com.celzero.bravedns.database.ProxyApplicationMapping import com.celzero.bravedns.util.LoggerConstants.Companion.LOG_TAG_PROXY +import ipn.Ipn import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -191,6 +192,14 @@ object ProxyManager : KoinComponent { io { proxyAppMappingRepository.removeAllWgProxies() } } + fun isProxied(proxyId: String): Boolean { + if (proxyId == "") return false + + // determine whether the connection is proxied or not + // if the connection is not Ipn.Base, Ipn.Block then it is proxied + return proxyId != Ipn.Base && proxyId != Ipn.Block + } + private fun io(f: suspend () -> Unit) { CoroutineScope(Dispatchers.IO).launch { f() } } diff --git a/app/src/main/java/com/celzero/bravedns/data/ConnTrackerMetaData.kt b/app/src/main/java/com/celzero/bravedns/data/ConnTrackerMetaData.kt index f49d0cf37..57113471f 100644 --- a/app/src/main/java/com/celzero/bravedns/data/ConnTrackerMetaData.kt +++ b/app/src/main/java/com/celzero/bravedns/data/ConnTrackerMetaData.kt @@ -26,6 +26,7 @@ data class ConnTrackerMetaData( val timestamp: Long, var isBlocked: Boolean, var blockedByRule: String, + var proxyDetails: String, var blocklists: String, val protocol: Int, var query: String, diff --git a/app/src/main/java/com/celzero/bravedns/database/ConnectionTracker.kt b/app/src/main/java/com/celzero/bravedns/database/ConnectionTracker.kt index b03990bb8..6fa71f788 100644 --- a/app/src/main/java/com/celzero/bravedns/database/ConnectionTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/database/ConnectionTracker.kt @@ -40,6 +40,7 @@ class ConnectionTracker { var isBlocked: Boolean = false var blockedByRule: String = "" var blocklists: String = "" + var proxyDetails: String = "" var flag: String = "" var dnsQuery: String? = null var timeStamp: Long = INIT_TIME_MS 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 c1972c6de..05fe81f22 100644 --- a/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt +++ b/app/src/main/java/com/celzero/bravedns/database/LogDatabase.kt @@ -28,7 +28,7 @@ import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SupportSQLiteDatabase import com.celzero.bravedns.util.Utilities -@Database(entities = [ConnectionTracker::class, DnsLog::class], version = 5, exportSchema = false) +@Database(entities = [ConnectionTracker::class, DnsLog::class], version = 6, exportSchema = false) @TypeConverters(Converters::class) abstract class LogDatabase : RoomDatabase() { @@ -62,6 +62,7 @@ abstract class LogDatabase : RoomDatabase() { .addMigrations(MIGRATION_2_3) .addMigrations(MIGRATION_3_4) .addMigrations(MIGRATION_4_5) + .addMigrations(MIGRATION_5_6) .build() } @@ -209,6 +210,15 @@ abstract class LogDatabase : RoomDatabase() { ) } } + + private val MIGRATION_5_6: Migration = + object : Migration(5, 6) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + "ALTER TABLE ConnectionTracker ADD COLUMN proxyDetails TEXT DEFAULT '' NOT NULL" + ) + } + } } fun checkPoint() { 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 afebdabf0..ecd950eae 100644 --- a/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt +++ b/app/src/main/java/com/celzero/bravedns/service/BraveVPNService.kt @@ -51,8 +51,8 @@ import com.celzero.bravedns.net.manager.ConnectionTracer import com.celzero.bravedns.receiver.NotificationActionReceiver import com.celzero.bravedns.service.FirewallManager.NOTIF_CHANNEL_ID_FIREWALL_ALERTS import com.celzero.bravedns.service.WireGuardManager.SEC_WARP_ID -import com.celzero.bravedns.ui.activity.HomeScreenActivity -import com.celzero.bravedns.ui.dialog.NotificationHandlerDialog +import com.celzero.bravedns.ui.HomeScreenActivity +import com.celzero.bravedns.ui.NotificationHandlerDialog import com.celzero.bravedns.util.* import com.celzero.bravedns.util.Constants.Companion.INIT_TIME_MS import com.celzero.bravedns.util.Constants.Companion.NOTIF_INTENT_EXTRA_ACCESSIBILITY_NAME @@ -67,6 +67,7 @@ import com.celzero.bravedns.util.Utilities.isUnspecifiedIp import com.celzero.bravedns.util.Utilities.showToastUiCentered import com.google.common.collect.Sets import dnsx.Dnsx +import dnsx.RDNS import dnsx.Summary import inet.ipaddr.HostName import inet.ipaddr.IPAddressString @@ -246,7 +247,7 @@ class BraveVPNService : dstIp: String, dstPort: Int, connId: String - ): Boolean { + ): ConnTrackerMetaData? { val connInfo = createConnTrackerMetaData( uid, @@ -1426,14 +1427,20 @@ class BraveVPNService : PersistentState.INTERNET_PROTOCOL -> { io("chooseIpVersion") { notifyConnectionMonitor() - restartVpn(createNewTunnelOptsObj()) + VpnController.mutex.withLock { + vpnAdapter?.setRouteLocked(appConfig.getInternetProtocol().value()) + } } } PersistentState.PROTOCOL_TRANSLATION -> { io("forceV4Egress") { setTunMode() } } PersistentState.DEFAULT_DNS_SERVER -> { - io("defaultDnsServer") { restartVpn(createNewTunnelOptsObj()) } + io("defaultDnsServer") { + VpnController.mutex.withLock { + vpnAdapter?.makeDefaultTransportLocked(persistentState.defaultDnsUrl) + } + } } PersistentState.PCAP_MODE -> { io("pcap") { setPcapMode() } @@ -1479,7 +1486,7 @@ class BraveVPNService : // no-op } AppConfig.ProxyProvider.TCP -> { - // yet to implement + VpnController.mutex.withLock { vpnAdapter?.setTcpProxyLocked() } } AppConfig.ProxyProvider.WIREGUARD -> { // update wireguard config @@ -1488,14 +1495,12 @@ class BraveVPNService : } } AppConfig.ProxyProvider.ORBOT -> { - // restart vpn as it requires app to be excluded from vpn - restartVpn(createNewTunnelOptsObj()) + // update orbot config, its treated as SOCKS5 or HTTP proxy internally + VpnController.mutex.withLock { vpnAdapter?.setCustomProxyLocked(tunProxyMode) } } AppConfig.ProxyProvider.CUSTOM -> { // custom either means socks5 or http proxy - // restart vpn as it requires app to be excluded from vpn - // further optimization: restart vpn only if app is selected as part of proxy - restartVpn(createNewTunnelOptsObj()) + VpnController.mutex.withLock { vpnAdapter?.setCustomProxyLocked(tunProxyMode) } } } } @@ -1514,9 +1519,7 @@ class BraveVPNService : } private fun spawnLocalBlocklistStampUpdate() { - io("dnsStampUpdate") { - VpnController.mutex.withLock { vpnAdapter?.setBraveDnsStampLocked() } - } + io("dnsStampUpdate") { VpnController.mutex.withLock { vpnAdapter?.setRDNSStampLocked() } } } private suspend fun notifyConnectionMonitor() { @@ -2363,27 +2366,45 @@ class BraveVPNService : // provides the hexadecimal value as a string for connId val connId = Utilities.getRandomString(8) - val isBlocked = + // connection tracker is null in case + val connTracker = if (realIps?.isEmpty() == true || d?.isEmpty() == true) { block(protocol, uid, srcIp, srcPort, dstIp, dstPort, connId) } else { blockAlg(protocol, uid, src, dest, realIps, d, blocklists, connId) } - if (isBlocked) { + if (connTracker == null) { + // dns-request (isDns(dstPort) && isVpnDns(dstIp)), return Ipn.Base, + // no need to check for other proxies + return persistAndConstructFlowResponse(connTracker, Ipn.Base, connId, uid) + } + + if (connTracker.isBlocked) { // return Ipn.Block, no need to check for other rules if (DEBUG) Log.d(LOG_TAG_VPN, "flow: received rule: block, returning Ipn.Block, $connId, $uid") - return getFlowResponseString(Ipn.Block, connId, uid) - } else { - trackedCids.add(connId) + return persistAndConstructFlowResponse(connTracker, Ipn.Block, connId, uid) } + // add to trackedCids, so that the connection can be removed from the list when the + // connection is closed (onTCPSocketClosed, onUDPSocketClosed, onICMPClosed) + // use: ui to show the active connections + trackedCids.add(connId) + + return determineProxyDetails(connTracker, connId, uid) + } + + private fun determineProxyDetails( + connTracker: ConnTrackerMetaData?, + connId: String, + uid: Int + ): String { // if no proxy is enabled, return Ipn.Base if (!appConfig.isProxyEnabled()) { if (DEBUG) Log.d(LOG_TAG_VPN, "flow: no proxy enabled, returning Ipn.Base, $connId, $uid") - return getFlowResponseString(Ipn.Base, connId, uid) + return persistAndConstructFlowResponse(connTracker, Ipn.Base, connId, uid) } // check for other proxy rules @@ -2408,7 +2429,7 @@ class BraveVPNService : LOG_TAG_VPN, "flow: wireguard is enabled and app is included, returning $proxyId, $connId, $uid" ) - return getFlowResponseString(proxyId, connId, uid) + return persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) } } @@ -2419,12 +2440,13 @@ class BraveVPNService : Log.e(LOG_TAG_VPN, "flow: tcp proxy is enabled but app is not included") // pass-through } else { - val ip = realDestIp.ifEmpty { dstIp } + val ip = connTracker?.destIP ?: "" val isCloudflareIp = TcpProxyHelper.isCloudflareIp(ip) - Log.d( - LOG_TAG_VPN, - "flow: tcp proxy enabled, checking for cloudflare: $realDestIp, $isCloudflareIp" - ) + if (DEBUG) + Log.d( + LOG_TAG_VPN, + "flow: tcp proxy enabled, checking for cloudflare: $ip, $isCloudflareIp" + ) if (isCloudflareIp) { val proxyId = "${Ipn.WG}${SEC_WARP_ID}" if (DEBUG) @@ -2432,14 +2454,19 @@ class BraveVPNService : LOG_TAG_VPN, "flow: tcp proxy enabled, but destination is cloudflare, returning $proxyId, $connId, $uid" ) - return getFlowResponseString(proxyId, connId, uid) + return persistAndConstructFlowResponse(connTracker, proxyId, connId, uid) } if (DEBUG) Log.d( LOG_TAG_VPN, "flow: tcp proxy enabled, returning ${ProxyManager.ID_TCP_BASE}, $connId, $uid" ) - return getFlowResponseString(ProxyManager.ID_TCP_BASE, connId, uid) + return persistAndConstructFlowResponse( + connTracker, + ProxyManager.ID_TCP_BASE, + connId, + uid + ) } } @@ -2454,7 +2481,12 @@ class BraveVPNService : LOG_TAG_VPN, "flow: received rule: orbot, returning ${ProxyManager.ID_ORBOT_BASE}, $connId, $uid" ) - return getFlowResponseString(ProxyManager.ID_ORBOT_BASE, connId, uid) + return persistAndConstructFlowResponse( + connTracker, + ProxyManager.ID_ORBOT_BASE, + connId, + uid + ) } } @@ -2465,7 +2497,12 @@ class BraveVPNService : LOG_TAG_VPN, "flow: rule: socks5, use ${ProxyManager.ID_S5_BASE}, $connId, $uid" ) - return getFlowResponseString(ProxyManager.ID_S5_BASE, connId, uid) + return persistAndConstructFlowResponse( + connTracker, + ProxyManager.ID_S5_BASE, + connId, + uid + ) } if (appConfig.isCustomHttpProxyEnabled()) { @@ -2474,11 +2511,16 @@ class BraveVPNService : LOG_TAG_VPN, "flow: rule: http, use ${ProxyManager.ID_HTTP_BASE}, $connId, $uid" ) - return getFlowResponseString(ProxyManager.ID_HTTP_BASE, connId, uid) + return persistAndConstructFlowResponse( + connTracker, + ProxyManager.ID_HTTP_BASE, + connId, + uid + ) } if (DEBUG) Log.d(LOG_TAG_VPN, "flow: no proxy enabled2, returning Ipn.Base, $connId, $uid") - return getFlowResponseString(Ipn.Base, connId, uid) + return persistAndConstructFlowResponse(connTracker, Ipn.Base, connId, uid) } fun hasCid(connId: String): Boolean { @@ -2499,7 +2541,23 @@ class BraveVPNService : io("refreshWg") { VpnController.mutex.withLock { vpnAdapter?.refreshProxiesLocked() } } } - private fun getFlowResponseString(proxyId: String, connId: String, uid: Int): String { + suspend fun getRDNS(type: RethinkBlocklistManager.RethinkBlocklistType): RDNS? { + return VpnController.mutex.withLock { vpnAdapter?.getRDNS(type) } + } + + private fun persistAndConstructFlowResponse( + connTracker: ConnTrackerMetaData?, + proxyId: String, + connId: String, + uid: Int + ): String { + // persist the connTracker + if (connTracker != null) { + connTracker.proxyDetails = proxyId + netLogTracker.writeIpLog(connTracker) + if (DEBUG) Log.d(LOG_TAG_VPN, "flow: connTracker: $connTracker") + } + // "proxyId, connId, uid" return StringBuilder() .apply { @@ -2521,15 +2579,15 @@ class BraveVPNService : d: String?, blocklists: String, connId: String - ): Boolean { + ): ConnTrackerMetaData? { Log.d(LOG_TAG_VPN, "block-alg: $uid, $src, $dest, $realIps, $d, $blocklists") - if (d == null) return true + if (d == null) return null // TODO: handle multiple domains, for now, use the first domain val domains: Set = d.split(",").toSet() if (domains.isEmpty()) { Log.w(LOG_TAG_VPN, "block-alg domains are empty") - return true + return null } val first = HostName(src) @@ -2557,6 +2615,7 @@ class BraveVPNService : realDestIp, dstPort, protocol, + proxyDetails = "", // set later blocklists, domains.first(), connId @@ -2569,7 +2628,7 @@ class BraveVPNService : metadata: ConnTrackerMetaData, anyRealIpBlocked: Boolean = false, blocklists: String = "" - ): Boolean { + ): ConnTrackerMetaData? { // skip the block-ceremony for dns conns Log.d( LOG_TAG_VPN, @@ -2577,7 +2636,7 @@ class BraveVPNService : ) if (isDns(metadata.destPort) && isVpnDns(metadata.destIP)) { if (DEBUG) Log.d(LOG_TAG_VPN, "firewall-rule dns-request no-op on conn $metadata") - return false + return null } val rule = firewall(metadata, anyRealIpBlocked) @@ -2588,12 +2647,7 @@ class BraveVPNService : val blocked = FirewallRuleset.ground(rule) metadata.isBlocked = blocked - if (DEBUG) Log.d(LOG_TAG_VPN, "firewall-rule $rule on conn $metadata") - - // write to conntrack, written in background - connTrack(metadata) - - return blocked + return metadata } private fun createConnTrackerMetaData( @@ -2603,6 +2657,7 @@ class BraveVPNService : dstIp: String, dstPort: Int, protocol: Int, + proxyDetails: String = "", blocklists: String = "", query: String = "", connId: String @@ -2626,6 +2681,7 @@ class BraveVPNService : System.currentTimeMillis(), false, /*blocked?*/ "", /*rule*/ + proxyDetails, blocklists, protocol, query, diff --git a/app/src/main/java/com/celzero/bravedns/service/IPTracker.kt b/app/src/main/java/com/celzero/bravedns/service/IPTracker.kt index 82a7dda48..302e87dd7 100644 --- a/app/src/main/java/com/celzero/bravedns/service/IPTracker.kt +++ b/app/src/main/java/com/celzero/bravedns/service/IPTracker.kt @@ -55,6 +55,7 @@ internal constructor( connTracker.timeStamp = connTrackerMetaData.timestamp connTracker.blockedByRule = connTrackerMetaData.blockedByRule connTracker.blocklists = connTrackerMetaData.blocklists + connTracker.proxyDetails = connTrackerMetaData.proxyDetails connTracker.connId = connTrackerMetaData.connId val serverAddress = convertIpV6ToIpv4IfNeeded(connTrackerMetaData.destIP)