/* * Copyright (C) 2023 MURENA SAS * * 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 . */ package foundation.e.advancedprivacy.trackers.service import android.content.Intent import android.net.VpnService import android.os.Build import android.os.ParcelFileDescriptor import foundation.e.advancedprivacy.core.utils.notificationBuilder import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_TRACKER_FLAG import foundation.e.advancedprivacy.domain.entities.NotificationContent import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV4 import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV6 import foundation.e.advancedprivacy.trackers.service.Config.SESSION_NAME import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import org.koin.core.qualifier.named import org.koin.java.KoinJavaComponent.get import timber.log.Timber class TrackersService : VpnService() { companion object { var coroutineScope = CoroutineScope(Dispatchers.IO) } private val networkDNSAddressRepository: NetworkDNSAddressRepository = get(NetworkDNSAddressRepository::class.java) private val trackersSupervisor: TrackersSupervisorStandalone = get( TrackersSupervisor::class.java ) as TrackersSupervisorStandalone private val notificationTrackerFlag: NotificationContent = get(NotificationContent::class.java, named("notificationTrackerFlag")) override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { startVPN() startForeground( NOTIFICATION_TRACKER_FLAG, notificationBuilder( context = this, content = notificationTrackerFlag ).build() ) trackersSupervisor.mutableState.value = FeatureState.ON return START_STICKY } override fun onDestroy() { networkDNSAddressRepository.stop() trackersSupervisor.mutableState.value = FeatureState.OFF super.onDestroy() } private fun startVPN() { val vpnInterface = initVPN() if (vpnInterface != null) { networkDNSAddressRepository.start() coroutineScope = CoroutineScope(Dispatchers.IO) get(TunLooper::class.java).apply { listenJob(vpnInterface, coroutineScope) } } else { Timber.e("Cannot get VPN interface") } } private fun initVPN(): ParcelFileDescriptor? { val builder = Builder() builder.setSession(SESSION_NAME) // IPV4: builder .addAddress(Config.ADDRESS_IPV4, 24) .addDnsServer(Config.VIRTUALDNS_IPV4) .addRoute(Config.VIRTUALDNS_IPV4, 32) // IPV6 builder .addAddress(Config.ADDRESS_IPV6, 48) .addDnsServer(Config.VIRTUALDNS_IPV6) .addRoute(Config.VIRTUALDNS_IPV6, 128) DNS_SERVER_TO_CATCH_IPV4.forEach { builder.addRoute(it, 32) } DNS_SERVER_TO_CATCH_IPV6.forEach { builder.addRoute(it, 128) } // TODO: block private DNS. // TODO 20230821: seen in privateDNSFilter, bypass filter for google apps on Android 7/8 builder.addDisallowedApplication(packageName) builder.setBlocking(true) builder.setMtu(Config.MTU) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { builder.setMetered(false) // take over defaults from underlying network } return builder.establish() } }