Наблюдение за исходящими/входящими сетевыми пакетами относительно типа соединения (WiFi/сотовая связь)Android

Форум для тех, кто программирует под Android
Ответить Пред. темаСлед. тема
Anonymous
 Наблюдение за исходящими/входящими сетевыми пакетами относительно типа соединения (WiFi/сотовая связь)

Сообщение Anonymous »

У нас есть требование к VpnService в нашем приложении, который в основном разрешает трафик только на адреса из белого списка и от него и отбрасывает остальной трафик, а также позволяет время от времени обходить определенный адрес. Текущая реализация, которую мы имеем, основана на https://github.com/julian-klode/dns66/b ... d.java#L93 и адаптирована к также использовать CloudFlare в качестве DNS, когда пользователь использует сотовую связь вместо Wi-Fi.
Зависимость CloudFlare была добавлена, поскольку, по словам создателя нашего сервиса, они не могли обеспечить ту же функциональность при использовании сотовый. Однако наше приложение для iOS прекрасно справляется с этой задачей без каких-либо сторонних зависимостей, поэтому я задался вопросом, возможно ли что-то подобное и на Android с помощью встроенных инструментов.
Я наткнулся на официальные документы Google по адресу https://android.googlesource.com/platfo ... oid/toyvpn и мне было интересно, есть ли что-то вроде это работает независимо от типа сети.
Для справки, вот наша тема и сервис:
class SecureThread(private val vpnService: VpnService, private val context: Context) : Runnable {
companion object {
var allowBypass = false
}

private var dnsServer: InetAddress? = null
private var fileDescriptor: ParcelFileDescriptor? = null
private var thread: Thread? = null
private var inputStream: SecureFileInputStream? = null
private var whitelistedHosts = setOf(
"sample.com",
"second.io"
)

private val secureBuilder = SecureBuilder()

fun startThread() {
SecureService.status = SecureService.Status.STARTING
Timber.i("Starting Vpn Thread")
thread = Thread(this, "some label").apply {
start()
SecureService.status = SecureService.Status.RUNNING
}
}

fun stopThread() {
SecureService.status = SecureService.Status.STOPPING
thread?.interrupt()
inputStream?.interrupt()
thread?.join(2000)
thread = null
SecureService.status = SecureService.Status.STOPPED
}

override fun run() {
try {
var retryTimeout = SecureService.MIN_RETRY_TIME

while (true) {
try {
runVpn()
break
} catch (e: Exception) {
}

Thread.sleep(retryTimeout.toLong() * 1000)

retryTimeout = if (retryTimeout < SecureService.MAX_RETRY_TIME) {
retryTimeout * 2
} else {
retryTimeout
}
}
} catch (e: Exception) {
}
}

private fun runVpn() {
configure()

val stream = SecureFileInputStream(fileDescriptor!!.fileDescriptor)
inputStream = stream

val packet = ByteArray(32767)

val executor =
ThreadPoolExecutor(0, 32, 60L, TimeUnit.SECONDS, SynchronousQueue())

try {
while (true) {
val length = try {
inputStream!!.read(packet)
} catch (e: Exception) {
return
}

val readPacket = packet.copyOfRange(0, length)

val outFd = FileOutputStream(fileDescriptor!!.fileDescriptor)

val dnsSocket = DatagramSocket()
vpnService.protect(dnsSocket)

try {
executor.execute {
handleDnsRequest(readPacket, dnsSocket, outFd)
}
} catch (e: RejectedExecutionException) {
}
}
} finally {
executor.shutdownNow()
fileDescriptor!!.close()
fileDescriptor = null
}
}

private fun handleDnsRequest(
packet: ByteArray,
dnsSocket: DatagramSocket,
outStream: FileOutputStream
) {
try {
val parsedPacket = IpV4Packet.newPacket(packet, 0, packet.size)

if (parsedPacket.payload !is UdpPacket) {
return
}
val dnsRawData = (parsedPacket.payload as UdpPacket).payload.rawData
val dnsMsg = Message(dnsRawData)
if (dnsMsg.question == null) {
return
}
val dnsQueryName = dnsMsg.question.name.toString(true)

val response: ByteArray =
if (whitelistedHosts.any { dnsQueryName.contains(it) } || allowBypass) {
Timber.i("Secure:: pass request with $dnsQueryName, bypass: $allowBypass")

val outPacket = DatagramPacket(dnsRawData, 0, dnsRawData.size, dnsServer!!, 53)

try {
dnsSocket.send(outPacket)
} catch (e: Exception) {
throw e
}

val datagramData = ByteArray(1024)
val replyPacket = DatagramPacket(datagramData, datagramData.size)
dnsSocket.receive(replyPacket)

datagramData
} else {
Timber.i("Secure:: block request with $dnsQueryName")
dnsMsg.header.setFlag(Flags.QR.toInt())
dnsMsg.addRecord(
ARecord(
dnsMsg.question.name,
dnsMsg.question.dClass,
10.toLong(),
Inet4Address.getLocalHost()
), Section.ANSWER
)

dnsMsg.toWire()
}

val udpOutPacket = parsedPacket.payload as UdpPacket
val ipOutPacket = IpV4Packet.Builder(parsedPacket)
.srcAddr(parsedPacket.header.dstAddr)
.dstAddr(parsedPacket.header.srcAddr)
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(
UdpPacket.Builder(udpOutPacket)
.srcPort(udpOutPacket.header.dstPort)
.dstPort(udpOutPacket.header.srcPort)
.srcAddr(parsedPacket.header.dstAddr)
.dstAddr(parsedPacket.header.srcAddr)
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(
UnknownPacket.Builder()
.rawData(response)
)
).build()

try {
outStream.write(ipOutPacket.rawData)
} catch (e: Exception) {
throw e
}
} catch (e: Exception) {
} finally {
dnsSocket.close()
outStream.close()
}
}

private fun configure() {
dnsServer = InetUtils.getDnsServers(vpnService as Context)
val builder = secureBuilder.configure(vpnService, vpnService.Builder())

fileDescriptor = builder
.setSession("session label").establish()
}
}

Сервис:
class SecureService : VpnService(), Handler.Callback {
private var vpnThread: SecureThread = SecureThread(this, this)
private val connectivityChangedReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
onConnectivityChanged(intent)
}
}

private val stopBr: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (STOP_MESSAGE == intent.action) {
Timber.i("Secure: Stopping")
onRevoke()
}
}
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.i("Secure:: Start command")
startVpn()
registerReceiver(connectivityChangedReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
return Service.START_STICKY
}

override fun handleMessage(message: Message): Boolean {
return true
}

override fun onCreate() {
super.onCreate()

LocalBroadcastManager.getInstance(this).registerReceiver(
stopBr, IntentFilter(STOP_MESSAGE)
)
}

override fun onRevoke() {
stopVpn()
super.onRevoke()
}

override fun onDestroy() {
super.onDestroy()
stopVpn()
}

private fun startVpn() {
log("Stopping VPN service")
status = Status.STARTING
restartVpn()
}

private fun restartVpn() {
log("Restarting VPN service")
vpnThread.stopThread()
vpnThread.startThread()
}

private fun stopVpn() {
log("Stopping VPN service")
vpnThread.stopThread()
try {
unregisterReceiver(connectivityChangedReceiver)
} catch (_: IllegalArgumentException) {
}
status = Status.STOPPED
stopSelf()
}

private fun waitForNetwork() {
status = Status.WAITING_FOR_NETWORK
vpnThread.stopThread()
}

private fun onConnectivityChanged(intent: Intent) {
if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 0) == ConnectivityManager.TYPE_VPN) {
return
}

if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
log("No internet connection")
waitForNetwork()
} else {
status = Status.RECONNECTING
restartVpn()
}
}

private fun log(text: String) {
Timber.i("${TAG}$text")
}

companion object {
private const val TAG = "SecureService:: "
const val MIN_RETRY_TIME = 5
const val MAX_RETRY_TIME = 120
const val VPN_PERMISSION_REQUEST_CODE = 666
const val STOP_MESSAGE = "STOP"
var status = Status.STOPPED
}

enum class Status {
STARTING,
RUNNING,
STOPPING,
STOPPED,
RECONNECTING,
RECONNECTING_NETWORK_ERROR,
WAITING_FOR_NETWORK,
}

enum class SecureMessage {
ConnectivityChanged,
}
}


Подробнее здесь: https://stackoverflow.com/questions/790 ... ype-wifi-c
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Сервер Ubuntu с двумя сетевыми интерфейсом - Curl [закрыто]
    Anonymous » » в форуме Linux
    0 Ответы
    1 Просмотры
    Последнее сообщение Anonymous
  • Может ли OS.ListDir повесить с сетевыми дисками? Какой системный вызов он использует?
    Anonymous » » в форуме Python
    0 Ответы
    2 Просмотры
    Последнее сообщение Anonymous
  • Могу ли я создать IVR или чат-бот для iOS, который взаимодействует с _реальными_ входящими телефонными звонками?
    Anonymous » » в форуме IOS
    0 Ответы
    36 Просмотры
    Последнее сообщение Anonymous
  • Проблема с исходящими вызовами Twilio
    Anonymous » » в форуме C#
    0 Ответы
    16 Просмотры
    Последнее сообщение Anonymous
  • Использование if(!empty) с несколькими переменными, не входящими в массив
    Anonymous » » в форуме Php
    0 Ответы
    4 Просмотры
    Последнее сообщение Anonymous

Вернуться в «Android»