Брандмауэр для Android с VpnService. Ответы передаются, но исключение SocketTimeoutException

Я использую простой брандмауэр для Android с помощью VpnService . Мое приложение похоже на ToyVpnService , но оно не отправляет необработанные IP-пакеты на удаленный VPN-сервер, который отправит их своим адресатам.

Моя реализация находится здесь: https://bitbucket.org/MaksimDmitriev/norootfirewall/src/006f7c33cd1cd4055f372ed3a88664fe2a4be3dd/src/com/norootfw/NoRootFwService.java?at=unix

Могу ли я выполнять всю эту процедуру переадресации локально? Это то, что я пытаюсь реализовать.

Я инициализирую устройство TUN и его файловые дескрипторы:

mInterface = new Builder().setSession(getString(R.string.app_name)) .addAddress("10.0.2.1", 24) .addRoute("0.0.0.0", 1) .addRoute("128.0.0.0", 1) .establish(); in = new FileInputStream(mInterface.getFileDescriptor()); out = new FileOutputStream(mInterface.getFileDescriptor()); 

Я назначаю 0.0.0.0/1 и 128.0.0.0/1 устройству TUN, чтобы сделать его более предпочтительным, чем маршрут по умолчанию с 0.0.0.0/0. Я использовал 0.0.0.0/0 и столкнулся с тем же исключением, которое ниже.

И вот пример UDP-запроса.

1). Я прочитал IP-пакет с устройства TUN.

 05-06 00:46:52.749: D/UDPChecksum(31077): Sent == [69, 0, 0, 36, 0, 0, 64, 0, 64, 17, 108, 91, 10, 0, 2, 1, -64, -88, 1, -59, -53, 1, -50, -87, 0, 16, 89, -114, 85, 68, 80, 95, 68, 65, 84, 65] 

Пожалуйста, рассмотрите структуру пакетов IPv4 здесь. Например, первое число 69 (0100 0101 в двоичном формате) означает, что версия протокола IP составляет 4 (4 разряда высокого порядка). И 4 младших бита обозначают длину заголовка Интернета (IHL) в 32-битных словах.

2). Затем создайте защищенный DatagramSocket и отправьте данные ( без заголовков IP и UDP) на адрес назначения, который я прочитал бы из захваченного IP-пакета.

3). Я получаю ответ от удаленного компьютера и хочу отправить его обратно в приложение, которое инициализировало запрос.

4). Я меняю IP-адреса источника и назначения и номера портов в IP-пакете, вычисляю контрольную сумму заголовка IPv4 и контрольную сумму UDP (создав псевдо-заголовок IPv4).

 05-06 00:46:52.889: D/UDPChecksum(31077): mIpv4PseudoHeader == [-64, -88, 1, -59, 10, 0, 2, 1, 0, 17, 0, 14] 

5). После этого я устанавливаю вычисленные контрольные суммы в соответствующие индексы IP-пакета и записываю IP-пакет в выходной поток устройства TUN.

 05-06 00:46:52.889: D/UDPChecksum(31077): To TUN == [69, 0, 0, 34, 0, 0, 64, 0, 64, 17, 108, 93, -64, -88, 1, -59, 10, 0, 2, 1, -50, -87, -53, 1, 0, 14, -105, -72, 85, 68, 80, 95, 79, 75] 

Ответ достигает моего приложения. DatagramSocket который был заблокирован после вызова метода receive (), заполняет буфер, который я предоставляю.

  byte[] responseBuffer = new byte[RESPONSE_SIZE]; try { mDatagramSocket.send(mDatagramPacket); final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length); mDatagramSocket.receive(response); } catch (IOException e) { Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here. logException(e); } 

Но его сокет выдает исключение, когда превышен тайм-аут.

 05-05 23:46:58.389: E/CLIENT(20553): java.net.SocketTimeoutException 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:551) 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:509) 05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161) 05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169) 05-05 23:46:58.389: E/CLIENT(20553): at java.net.DatagramSocket.receive(DatagramSocket.java:250) 05-05 23:46:58.389: E/CLIENT(20553): at socket.client.MainActivity$UdpThread.run(MainActivity.java:195) 05-05 23:46:58.389: E/CLIENT(20553): Caused by: libcore.io.ErrnoException: recvfrom failed: EAGAIN (Try again) 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfromBytes(Native Method) 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfrom(Posix.java:141) 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164) 05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:506) 05-05 23:46:58.389: E/CLIENT(20553): ... 4 more 

Все работает без брандмауэра. Я прочитал эти обсуждения, но они не помогли:

  • https://stackoverflow.com/a/17820461/1065835

  • Брандмауэр Android с VpnService

Solutions Collecting From Web of "Брандмауэр для Android с VpnService. Ответы передаются, но исключение SocketTimeoutException"

Попробуйте добавить отсутствующий обработчик исключения SocketTimeoutException

  byte[] responseBuffer = new byte[RESPONSE_SIZE]; try { mDatagramSocket.send(mDatagramPacket); final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length); mDatagramSocket.receive(response); } catch (SocketTimeoutException e) { // ignore ; // continue; } catch (IOException e) { Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here. logException(e); } 

источник

Из recvfrom () call docs:

[EAGAIN] или [EWOULDBLOCK] Дескриптор файла сокета отмечен O_NONBLOCK, и никакие данные не ждут, чтобы их принимали ; Или MSG_OOB, и не доступны внеполосные данные, и либо дескриптор файла сокета отмечен O_NONBLOCK, либо сокет не поддерживает блокировку для ожидания внеполосных данных.

Упрощенное объяснение

Если в сокете нет сообщений, вызовы приема ждут сообщения, если сокет не блокируется (см. Fcntl (2)), и в этом случае возвращается значение -1, а внешняя переменная errno установлена ​​в EAGAIN

DatagramSocket не находится в режиме блокировки и кажется, что вы пытаетесь прочитать из сокета, и нет данных для чтения, вы уверены, что действительно получаете данные? Попробуйте очистить буфер для каждого полученного пакета.

Дело в том, что я использовал неправильный псевдо-заголовок IPv4 для вычисления контрольной суммы. Он не содержал ни заголовка UDP пакета, ни переданных данных. Поскольку я включил заголовок UDP и данные, я не видел исключения из исходного вопроса.

Введите описание изображения здесь