Подтвердить что ты не робот

Брандмауэр для 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). Затем создайте protected 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-пакет в out, выходной поток устройства 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

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

4b9b3361

Ответ 1

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

enter image description here

Ответ 2

Попробуйте добавить отсутствующий обработчик исключений 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);
    }

источник

Ответ 3

Из recvfrom() вызывать документы:

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

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

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

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