Commit fc79b526 authored by Daniel Wolf's avatar Daniel Wolf
Browse files

Merge branch 'master' into 'translations'

Master

See merge request !35
parents d16c9bbf 05d1303a
Pipeline #5057 passed with stages
in 1 minute and 59 seconds
......@@ -56,6 +56,8 @@ A list of some extraordinary people who contributed to this project:
- Turkish translation by Kemal Oktay Aktoğan
- Russian translation by [bruleto](https://t.me/bruleto)
- Dutch translation by Bas Koedijk
- Portuguese translation by Rafael W. Bohnenberger
- Indonesian translation by Gloeyisk
<br/>
<br/>
......@@ -68,6 +70,10 @@ This work is licensed under the GNU GPLv3 License. Different license-agreements
Copyright (C) 2019 Daniel Wolf
<br>
<br>
Please be aware that I'm not going to tolerate exact copies of this app on the Play Store. This project took a lot of work, not only from me, but many alpha/beta testers and translators. Copying is - per the license - generally allowed, but uploading a nearly identical version to the Play Store would be impersonation which I'd go after.
# Third-party content
This work contains third-party content, namely:
- [sentry-java](https://github.com/getsentry/sentry-java) for crash-reporting
......
......@@ -5,12 +5,12 @@ This is where you come in: I speak German and English but can't deliver any othe
# How it's done
I use an web-based translations editor called [Weblate](weblate.org) (open-source software, available under the GPLv3) to make managing translations easy.
I host my [own instance of Weblate](https://weblate.frostnerd.com) -- [creating an account](https://weblate.frostnerd.com/accounts/register/) is free and neither does the site contain ads, nor does it use any tracking.
I host my [own instance of Weblate](https://weblate.frostnerd.com) -- [creating an account](https://weblate.frostnerd.com/accounts/register/) is free and neither does the site contain ads, nor does it use any tracking (look at the privacy policy below though!).
See below for a quick tutorial on how to use Weblate.
# The current state
Right now the app only contains German and English translations. Your help is needed.
Right now the app contains German, English, Dutch, Portuguese, Russian, Indonesian and Turkish translations. This is already a good number of translations, but your help is needed to push it even further.
<a href="https://weblate.frostnerd.com/engage/nebulo/?utm_source=widget">
<img src="https://weblate.frostnerd.com/widgets/nebulo/-/multi-green.svg" alt="Translation state" />
......@@ -30,7 +30,7 @@ It is analyzed and [publicly displayed](https://weblate.frostnerd.com/stats/) wh
No collected data is shared with any third party.
No collected data is analyzed automatically except for the usages described above.
You can remove your account at any time. Simply do this by opening [this page](https://weblate.frostnerd.com/accounts/remove/) whilst logged in. You'll receive an e-mail with a link you have to click to delete your account. Deleting your account deletes your profile, completely removing your e-mail address from the database. Your IP addresses used in the past won't be deleted and stay in the audit log.
You can remove your account at any time. Simply do this by opening [this page](https://weblate.frostnerd.com/accounts/remove/) whilst logged in. You'll receive an e-mail with a link you have to click to delete your account. Deleting your account deletes your profile, completely removing your e-mail address from the database. Your IP addresses used in the past won't be deleted and stay in the audit log. Translations contributed by you won't be deleted as well.
Feel free to contact me at daniel.wolf@frostnerd.com if you wish to know what data is saved about you, or if you wish it to be deleted.
......
......@@ -25,7 +25,7 @@ android {
applicationId "com.frostnerd.smokescreen"
minSdkVersion 21
targetSdkVersion 28
versionCode 42
versionCode 43
versionName "1.0-Beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
......@@ -74,7 +74,7 @@ android {
adblocker {
dimension "version"
versionNameSuffix "-adblock"
versionCode 42
versionCode 43
}
normal {
dimension "version"
......@@ -107,7 +107,7 @@ dependencies {
implementation 'com.frostnerd.utilskt:preferences:1.5.14' // https://git.frostnerd.com/AndroidUtils/preferenceskt (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.3.19' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.134' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.135' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:general:1.0.16' // https://git.frostnerd.com/AndroidUtils/generalkt (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:adapters:1.1.1' // https://git.frostnerd.com/AndroidUtils/Adapters (Accessible after logging in [free of charge])
......
{
"formatVersion": 1,
"database": {
"version": 10,
"identityHash": "daac9efbbf9d89b9aa10f7ad54ac736a",
"entities": [
{
"tableName": "CachedResponse",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dnsName` TEXT NOT NULL, `type` INTEGER NOT NULL, `records` TEXT NOT NULL, PRIMARY KEY(`dnsName`, `type`))",
"fields": [
{
"fieldPath": "dnsName",
"columnName": "dnsName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "records",
"columnName": "records",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"dnsName",
"type"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DnsQuery",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `askedServer` TEXT, `fromCache` INTEGER NOT NULL, `questionTime` INTEGER NOT NULL, `responseTime` INTEGER NOT NULL, `responses` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "askedServer",
"columnName": "askedServer",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fromCache",
"columnName": "fromCache",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "questionTime",
"columnName": "questionTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "responseTime",
"columnName": "responseTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "responses",
"columnName": "responses",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DnsRule",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stagingType` INTEGER NOT NULL, `type` INTEGER NOT NULL, `host` TEXT NOT NULL, `target` TEXT NOT NULL, `ipv6Target` TEXT, `importedFrom` INTEGER, `isWildcard` INTEGER NOT NULL, FOREIGN KEY(`importedFrom`) REFERENCES `HostSource`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "stagingType",
"columnName": "stagingType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "target",
"columnName": "target",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ipv6Target",
"columnName": "ipv6Target",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "importedFrom",
"columnName": "importedFrom",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isWildcard",
"columnName": "isWildcard",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_DnsRule_importedFrom",
"unique": false,
"columnNames": [
"importedFrom"
],
"createSql": "CREATE INDEX `index_DnsRule_importedFrom` ON `${TABLE_NAME}` (`importedFrom`)"
},
{
"name": "index_DnsRule_host_type_stagingType",
"unique": true,
"columnNames": [
"host",
"type",
"stagingType"
],
"createSql": "CREATE UNIQUE INDEX `index_DnsRule_host_type_stagingType` ON `${TABLE_NAME}` (`host`, `type`, `stagingType`)"
}
],
"foreignKeys": [
{
"table": "HostSource",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"importedFrom"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "HostSource",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER NOT NULL, `ruleCount` INTEGER, `name` TEXT NOT NULL, `source` TEXT NOT NULL, `whitelistSource` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ruleCount",
"columnName": "ruleCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "whitelistSource",
"columnName": "whitelistSource",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'daac9efbbf9d89b9aa10f7ad54ac736a')"
]
}
}
\ No newline at end of file
......@@ -37,7 +37,7 @@ import com.frostnerd.smokescreen.database.repository.HostSourceRepository
@Database(entities = [CachedResponse::class, DnsQuery::class, DnsRule::class, HostSource::class], version = AppDatabase.currentVersion)
abstract class AppDatabase : RoomDatabase() {
companion object {
const val currentVersion:Int = 9
const val currentVersion:Int = 10
}
abstract fun cachedResponseDao(): CachedResponseDao
......
......@@ -60,7 +60,7 @@ val MIGRATION_5_6 = migration(5, 6) {
Logger.logIfOpen("DB_MIGRATION", "Migrating from 5 to 6")
it.execSQL("CREATE TABLE IF NOT EXISTS `DnsRule` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stagingType` INTEGER NOT NULL, `type` INTEGER NOT NULL, `host` TEXT NOT NULL, `target` TEXT NOT NULL, `ipv6Target` TEXT, `importedFrom` INTEGER, FOREIGN KEY(`importedFrom`) REFERENCES `HostSource`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
it.execSQL("CREATE TABLE IF NOT EXISTS `HostSource` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER NOT NULL, `name` TEXT NOT NULL, `source` TEXT NOT NULL)")
it.execSQL("CREATE INDEX `index_DnsRule_importedFrom` ON `DnsRule` (`importedFrom`)")
it.execSQL("CREATE INDEX IF NOT EXISTS `index_DnsRule_importedFrom` ON `DnsRule` (`importedFrom`)")
it.execSQL("CREATE UNIQUE INDEX `index_DnsRule_host_type_stagingType` ON `DnsRule` (`host`, `type`, `stagingType`)")
Logger.logIfOpen("DB_MIGRATION", "Migration from 5 to 6 completed")
}
......@@ -89,13 +89,19 @@ val MIGRATION_8_9 = migration(8, 9) {
it.execSQL("ALTER TABLE `HostSource` ADD COLUMN `ruleCount` INTEGER")
Logger.logIfOpen("DB_MIGRATION", "Migration from 8 to 9 completed")
}
@VisibleForTesting
val MIGRATION_9_10 = migration(9, 10) {
Logger.logIfOpen("DB_MIGRATION", "Migrating from 9 to 10")
it.execSQL("ALTER TABLE `DnsRule` ADD COLUMN `isWildcard` INTEGER NOT NULL DEFAULT 0")
Logger.logIfOpen("DB_MIGRATION", "Migration from 9 to 10 completed")
}
fun Context.getDatabase(): AppDatabase {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "data")
.allowMainThreadQueries()
.addMigrations(MIGRATION_2_X, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9)
.addMigrations(MIGRATION_2_X, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10)
.build()
}
return INSTANCE!!
......
......@@ -75,9 +75,12 @@ interface DnsRuleDao {
@Query("SELECT COUNT(*) FROM DnsRule WHERE importedFrom IS NOT NULL")
fun getNonUserCount(): Long
@Query("SELECT CASE WHEN :type=28 THEN IFNULL(ipv6Target, target) ELSE target END FROM DnsRule WHERE host=:host AND target != '' AND (type = :type OR type=255) AND (importedFrom is NULL OR IFNULL((SELECT enabled FROM HostSource h WHERE h.id=importedFrom),0) = 1) AND (importedFrom IS NOT NULL OR :useUserRules=1) AND (SELECT COUNT(*) FROM DnsRule WHERE target='' AND host=:host)=0 LIMIT 1")
@Query("SELECT CASE WHEN :type=28 THEN IFNULL(ipv6Target, target) ELSE target END FROM DnsRule d1 WHERE d1.host=:host AND d1.target != '' AND (d1.type = :type OR d1.type=255) AND (d1.importedFrom is NULL OR IFNULL((SELECT enabled FROM HostSource h WHERE h.id=d1.importedFrom),0) = 1) AND (d1.importedFrom IS NOT NULL OR :useUserRules=1) AND (SELECT COUNT(*) FROM DnsRule d2 WHERE d2.target='' AND d2.host=:host AND (d2.type = :type OR d2.type=255) AND (d2.importedFrom IS NOT NULL OR :useUserRules=1) AND (importedFrom is NULL OR IFNULL((SELECT enabled FROM HostSource h WHERE h.id=importedFrom),0)))=0 AND isWildcard=0 LIMIT 1")
fun findRuleTarget(host: String, type: Record.TYPE, useUserRules:Boolean): String?
@Query("SELECT * FROM DnsRule d1 WHERE ((:includeWhitelistEntries=1 AND d1.target == '') OR (:includeNonWhitelistEntries=1 AND d1.target!='')) AND (d1.type = :type OR d1.type=255) AND (d1.importedFrom is NULL OR IFNULL((SELECT enabled FROM HostSource h WHERE h.id=d1.importedFrom),0) = 1) AND (d1.importedFrom IS NOT NULL OR :useUserRules=1) AND :host LIKE host AND isWildcard=1")
fun findPossibleWildcardRuleTarget(host: String, type: Record.TYPE, useUserRules:Boolean, includeWhitelistEntries:Boolean, includeNonWhitelistEntries:Boolean):List<DnsRule>
@Query("DELETE FROM DnsRule WHERE importedFrom=:sourceId")
fun deleteAllFromSource(sourceId: Long)
......
......@@ -38,7 +38,8 @@ data class DnsRule(
val host: String,
val target: String,
val ipv6Target:String? = null,
val importedFrom: Long? = null
val importedFrom: Long? = null,
var isWildcard:Boolean = false
) {
@PrimaryKey(autoGenerate = true) var id: Long = 0
......
......@@ -11,6 +11,7 @@ import kotlinx.android.synthetic.main.dialog_create_dnsrule.view.*
import org.minidns.record.Record
import java.net.Inet4Address
import java.net.Inet6Address
import java.util.regex.Matcher
/*
* Copyright (C) 2019 Daniel Wolf (Ch4t4r)
......@@ -33,6 +34,19 @@ import java.net.Inet6Address
class DnsRuleDialog(context: Context, dnsRule: DnsRule? = null, onRuleCreated: (DnsRule) -> Unit) :
AlertDialog(context, context.getPreferences().theme.dialogStyle) {
private var isWhitelist = false
companion object {
private val matchers = mutableMapOf<String, Matcher>()
fun printableHost(host:String): String {
return host.replace("%%", "**").replace("%", "*")
}
fun databaseHostToMatcher(host:String):Matcher {
return matchers.getOrPut(host, {
host.replace("%%", ".*").replace("%", "[^.]*").toPattern().matcher("")
})
}
}
init {
val view = layoutInflater.inflate(R.layout.dialog_create_dnsrule, null, false)
......@@ -91,12 +105,21 @@ class DnsRuleDialog(context: Context, dnsRule: DnsRule? = null, onRuleCreated: (
Record.TYPE.AAAA, Record.TYPE.ANY -> view.ipv6Address.text.toString()
else -> null
}
var isWildcard = false
val host = view.host.text.toString().let {
if(it.contains("*")) {
isWildcard = true
it.replace("**", "%%").replace("*", "%")
} else it.replace(Regex("^www\\."), "")
}
val newRule = dnsRule?.copy(
type = type,
host = view.host.text.toString(),
host = host,
target = primaryTarget,
ipv6Target = secondaryTarget
) ?: DnsRule(type, view.host.text.toString(), primaryTarget, secondaryTarget)
ipv6Target = secondaryTarget,
isWildcard = isWildcard
) ?: DnsRule(type, host, primaryTarget, secondaryTarget, isWildcard = isWildcard)
onRuleCreated(
newRule
)
......@@ -115,12 +138,13 @@ class DnsRuleDialog(context: Context, dnsRule: DnsRule? = null, onRuleCreated: (
if (dnsRule != null) {
if (dnsRule.isWhitelistRule()) {
isWhitelist = true
view.host.setText(printableHost(dnsRule.host))
getButton(DialogInterface.BUTTON_NEUTRAL).text =
context.getString(R.string.dialog_newdnsrule_specify_address)
view.ipv4Til.visibility = View.GONE
view.ipv6Til.visibility = View.GONE
} else {
view.host.setText(dnsRule.host)
view.host.setText(printableHost(dnsRule.host))
when {
dnsRule.type == Record.TYPE.A -> {
view.ipv4Address.setText(dnsRule.target)
......
......@@ -552,7 +552,7 @@ class DnsRuleFragment : Fragment() {
fun display(rule:DnsRule) {
dnsRule = rule
text.text = rule.host
text.text = DnsRuleDialog.printableHost(rule.host)
whitelistIndicator.visibility = if(rule.isWhitelistRule()) View.VISIBLE else View.GONE
}
override fun destroy() {}
......
......@@ -8,6 +8,7 @@ import android.net.Uri
import android.net.VpnService
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
......@@ -19,14 +20,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.frostnerd.dnstunnelproxy.DnsServerInformation
import com.frostnerd.general.service.isServiceRunning
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.*
import com.frostnerd.smokescreen.activity.SpeedTestActivity
import com.frostnerd.smokescreen.dialog.ServerChoosalDialog
import com.frostnerd.smokescreen.getPreferences
import com.frostnerd.smokescreen.registerLocalReceiver
import com.frostnerd.smokescreen.service.Command
import com.frostnerd.smokescreen.service.DnsVpnService
import com.frostnerd.smokescreen.unregisterLocalReceiver
import kotlinx.android.synthetic.main.fragment_main.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
......@@ -80,6 +78,30 @@ class MainFragment : Fragment() {
}
updateVpnIndicators()
}
startButton.setOnTouchListener { _, event ->
if(proxyRunning || proxyStarting) {
false
} else {
if(event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0) {
if(event.action == MotionEvent.ACTION_UP) {
if(VpnService.prepare(context!!) != null) {
showInfoTextDialog(context!!,
getString(R.string.dialog_overlaydetected_title),
getString(R.string.dialog_overlaydetected_message),
positiveButton = null,
negativeButton = null,
neutralButton = getString(android.R.string.ok) to { dialog, _ ->
dialog.dismiss()
startVpn()
proxyStarting = true
}
)
true
} else false
} else false
}else false
}
}
speedTest.setOnClickListener {
startActivity(Intent(context!!, SpeedTestActivity::class.java))
}
......
......@@ -28,6 +28,7 @@ import com.frostnerd.smokescreen.activity.PinActivity
import com.frostnerd.smokescreen.activity.PinType
import com.frostnerd.smokescreen.database.entities.CachedResponse
import com.frostnerd.smokescreen.database.getDatabase
import com.frostnerd.smokescreen.dialog.DnsRuleDialog
import com.frostnerd.smokescreen.util.DeepActionState
import com.frostnerd.smokescreen.util.Notifications
import com.frostnerd.smokescreen.util.preferences.VpnServiceState
......@@ -35,6 +36,7 @@ import com.frostnerd.smokescreen.util.proxy.ProxyBypassHandler
import com.frostnerd.smokescreen.util.proxy.ProxyHttpsHandler
import com.frostnerd.smokescreen.util.proxy.ProxyTlsHandler
import com.frostnerd.smokescreen.util.proxy.SmokeProxy
import com.frostnerd.vpntunnelproxy.FutureAnswer
import com.frostnerd.vpntunnelproxy.TrafficStats
import com.frostnerd.vpntunnelproxy.VPNTunnelProxy
import kotlinx.coroutines.*
......@@ -782,13 +784,18 @@ class DnsVpnService : VpnService(), Runnable {
log("DnsProxy created, creating VPN proxy")
vpnProxy = VPNTunnelProxy(dnsProxy!!, vpnService = this, coroutineScope = CoroutineScope(
newFixedThreadPoolContext(2, "proxy-pool")), logger = object:com.frostnerd.vpntunnelproxy.Logger() {
override fun failedRequest(question: FutureAnswer, reason: Throwable?) {
if(reason != null) log("A request failed: " + Logger.stacktraceToString(reason), "VPN-LIBRARY")
}
val advancedLogging = getPreferences().advancedLogging
override fun logException(ex: Exception, terminal: Boolean, level: Level) {
if(terminal) log(ex)
else log(Logger.stacktraceToString(ex), "VPN-LIBRARY, $level")
}
override fun logMessage(message: String, level: Level) {
if(level >= Level.INFO || (BuildConfig.DEBUG && level >= Level.FINE)) {
if(level >= Level.INFO || ((BuildConfig.DEBUG || advancedLogging) && level >= Level.FINE)) {
log(message, "VPN-LIBRARY, $level")
}
}
......@@ -962,9 +969,37 @@ class DnsVpnService : VpnService(), Runnable {
}
}
if (resolveResult != null) {
resolveResults[question] = resolveResult
true
} else false
val isWildcardWhitelisted = dao.findPossibleWildcardRuleTarget(uniformQuestion, question.type, useUserRules, true, false).any {
DnsRuleDialog.databaseHostToMatcher(it.host).reset(uniformQuestion).matches()
}
if(!isWildcardWhitelisted) {
resolveResults[question] = resolveResult
true
} else false
} else {
val wildcardResolveResults = dao.findPossibleWildcardRuleTarget(uniformQuestion, question.type, useUserRules, true, true).filter {
DnsRuleDialog.databaseHostToMatcher(it.host).reset(uniformQuestion).matches()