diff options
Diffstat (limited to 'trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt')
-rw-r--r-- | trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt new file mode 100644 index 0000000..6a2b218 --- /dev/null +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 MURENA SAS + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package foundation.e.advancedprivacy.trackers.service + +import android.net.LocalServerSocket +import android.system.ErrnoException +import android.system.Os +import android.system.OsConstants +import foundation.e.advancedprivacy.core.utils.runSuspendCatching +import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import timber.log.Timber +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.PrintWriter + +class DNSBlocker( + val filterHostnameUseCase: FilterHostnameUseCase +) { + private var resolverReceiver: LocalServerSocket? = null + + companion object { + private const val SOCKET_NAME = "foundation.e.advancedprivacy" + } + + private fun closeSocket() { + // Known bug and workaround that LocalServerSocket::close is not working well + // https://issuetracker.google.com/issues/36945762 + if (resolverReceiver != null) { + try { + Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR) + resolverReceiver!!.close() + resolverReceiver = null + } catch (e: ErrnoException) { + if (e.errno != OsConstants.EBADF) { + Timber.w("Socket already closed") + } else { + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") + } + } catch (e: Exception) { + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") + } + } + } + + fun listenJob(scope: CoroutineScope): Job = scope.launch(Dispatchers.IO) { + val resolverReceiver = runSuspendCatching { + LocalServerSocket(SOCKET_NAME) + }.getOrElse { + Timber.e(it, "Exception: cannot open DNS port on $SOCKET_NAME") + return@launch + } + + this@DNSBlocker.resolverReceiver = resolverReceiver + Timber.d("DNSFilterProxy running on port $SOCKET_NAME") + + while (isActive) { + runSuspendCatching { + val socket = resolverReceiver.accept() + val reader = BufferedReader(InputStreamReader(socket.inputStream)) + val line = reader.readLine() + val params = line.split(",").toTypedArray() + val output = socket.outputStream + val writer = PrintWriter(output, true) + val domainName = params[0] + val appUid = params[1].toInt() + if (filterHostnameUseCase.shouldBlock(domainName, appUid)) { + writer.println("block") + } else { + writer.println("pass") + } + socket.close() + }.onFailure { + if (it is CancellationException) { + closeSocket() + throw it + } else { + Timber.w(it, "Exception while listening DNS resolver") + } + } + } + } +} |