Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A firewall for Android with VpnService. Responses are delivered, but a SocketTimeoutException is thrown

Tags:

I am implementing a simple firewall for Android using VpnService. My app is similar to ToyVpnService, but it doesn't send raw IP packets to a remote VPN server which would forward them to their destinations.

My implementation is here: https://bitbucket.org/MaksimDmitriev/norootfirewall/src/006f7c33cd1cd4055f372ed3a88664fe2a4be3dd/src/com/norootfw/NoRootFwService.java?at=unix

Can I do all this forwarding routine locally? That's what I'm trying to implement.

I initialize a TUN device and its file descriptors:

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()); 

I assign 0.0.0.0/1 and 128.0.0.0/1 to the TUN device to make it more preferable than the default route with 0.0.0.0/0. I used 0.0.0.0/0 and ran into the same exception which is below.

And here is a sample UDP request.

1). I read an IP packet from the TUN device.

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] 

Please consider the IPv4 packet structure here. For example, the first number 69 (0100 0101 in binary) means that the version of the IP protocol is 4 (4 high-order bits). And the 4 low-order bits stand for the Internet Header Length (IHL) in 32-bit words.

2). Then a create a protected DatagramSocket and send the data (without its IP and UDP headers) to the destination address I'd read from the captured IP packet.

3). I receive a response from the remote machine and want to send it back to the app which initialized the request.

4). I swap the source and destination IP addresses and port numbers in the IP packet, calculate the IPv4 header checksum and the UDP checksum (having constructed an IPv4 pseudo header).

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

5). Afterwards I set the calculated checksums to the corresponding indexes of the IP packet and write the IP packet to out, the output stream of the TUN device.

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] 

The response reaches my app. The DatagramSocket which has been blocked after calling its receive() method fills the buffer I provide.

        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);         } 

But its socket throws an exception when the timeout is exceeded.

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 

Everything works properly without the firewall. I read these discussions, but they weren't helpful:

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

  • Android firewall with VpnService

like image 706
Maksim Dmitriev Avatar asked May 05 '15 23:05

Maksim Dmitriev


2 Answers

Try adding the missing SocketTimeoutException exception handler

   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);     } 

source

like image 184
Pat Avatar answered Oct 04 '22 11:10

Pat


From recvfrom() call docs:

[EAGAIN] or [EWOULDBLOCK] The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is available and either the socket's file descriptor is marked O_NONBLOCK or the socket does not support blocking to await out-of-band data.

simplified explanation

If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno set to EAGAIN

The DatagramSocket is not in blocking mode and seems like you are trying to read from the socket and there is no data to read, are you sure you are really receiving the data? try cleaning the buffer for each received packet.

like image 35
vzamanillo Avatar answered Oct 04 '22 11:10

vzamanillo