androidsocketsandroid-4.0-ice-cream-sandwichvpnpacket-capture

Protect a socket in VpnService


I'm exploring the capabilities of Android's VpnService. Presently, I've built a very rudimentary request forwarder by essentially rebuilding the IP stack in user space: I read IP packets from the VpnService's input stream, parse them, and for connections I don't want to forward, I attempt to recreate those socket connections outside the VPN connection.

I've understood that this last bit is facilitated by VpnService.protect() and have tried implementing it as follows:

Socket socket = new Socket();
vpnService.protect(socket);
socket.connect(new InetSocketAddress(
        header.getDestinationAddress(),  // From my IP datagram header
        body.getDestinationPort()));     // From the TCP datagram header

Unfortunately, this approach is causing a loopback into the VPN interface.

Whereas the above code will simply block and eventually time out, I observe the loopback by calling Socket.connect(InetSocketAddress) from a separate thread; the connection comes straight back into my VpnService's input stream and the process repeats.

Needless to say, this causes a loop. I get the feeling that the reason for this is that at the time of socket creation (and subsequently, the call to VpnService.protect(Socket)), I haven't set the destination IP & port yet.

This seems to indeed be the case, as the by overriding VpnService.protect(Socket) and VpnService.protect(int) in my VpnService implementation and calling the supers in both cases returns false.

How can I properly protect a socket connection?


Solution

  • The following code works.

    Socket socket = SocketChannel.open().socket();
    if ((null != socket) && (null != vpnService)) {
        vpnService.protect(socket);
    }
    socket.connect(...);
    

    new Socket() doesn't have a valid file descriptor, so it cannot be protected.