Android VpnService转发数据包

时间:2016-05-17 04:25:59

标签: java android sockets tcp packet-capture

我想使用android' s VpnService来捕获根据IP地址过滤掉的数据包。我可以从" tun"获取数据包。接口很好,但之后我不知道如何将它们转发到原来的目的地。根据{{​​3}}的评论,似乎我只需要:

  1. 为目标IP地址和端口
  2. 创建新套接字
  3. 修剪IP和TCP标头以仅发送数据
  4. 收到回复时重新附加IP和TCP标头
  5. 将完整数据包发送到输出流
  6. 我试图发送这样的数据:

    Socket socket = new Socket();
    socket.bind(new InetSocketAddress(0));
    if (protect(socket)){
        Log.e(TAG, "Socket protected");
    }else{
        Log.e(TAG, "Socket NOT protected");
    }
    socket.connect(new InetSocketAddress(ipPacket.getDestinationIp(), ipPacket.getDstPort()));
    Log.e(TAG, "Socket connected: " + socket.isConnected());
    
    socket.getOutputStream().write(getTCPHeader(getIpHeader(packet)[1])[1].array());
    

    方法getTCPHeader(ByteArray packet)getIpHeader(ByteArray packet)只需将数据包拆分为两个ByteArray,如下所示:

    private ByteBuffer[] getIpHeader(ByteBuffer packet){
        packet.position(0);
        ByteBuffer ipHeader = ByteBuffer.allocate(20);
        ByteBuffer data = ByteBuffer.allocate(packet.limit() - 20);
    
        packet.get(ipHeader.array(), 0, 20);
    
        packet.get(data.array(), 0, packet.limit() - 20);
    
        return new ByteBuffer[]{ipHeader, data};
    }
    
    private ByteBuffer[] getTCPHeader(ByteBuffer packet){
        packet.position(20);
        ByteBuffer tcpHeader = ByteBuffer.allocate(20);
        ByteBuffer data = ByteBuffer.allocate(packet.limit() - 20);
    
        packet.get(tcpHeader.array(), 0, 20);
    
        packet.get(data.array(), 0, packet.limit() - 40);
    
        return new ByteBuffer[]{tcpHeader, data};
    }
    

    现在要从服务器获得响应,我使用以下代码:

    ByteBuffer responsePacket = ByteBuffer.allocate(65535);
    InputStream socketInputStream = socket.getInputStream();
    try{
        int responseLength = socketInputStream.read(responsePacket.array());
        if (responseLength > 20){
            Log.e(TAG, "===Server Response===");
            Log.e(TAG, "Length: " + responseLength);
    
            ByteBuffer trimmedResponseData = ByteBuffer.allocate(responseLength);
            System.arraycopy(responseData.array(), 0, trimmedResponseData.array(), 0, responseLength);
    
            String resp = "";
            for (int i = 0; i < responseLength; i++){
                resp += String.valueOf(responseData.get(i) + " ");
            }
    
            Log.e(TAG, "Response data: " + resp);
    
            ByteBuffer finalPacket = ByteBuffer.allocate(40 + responseLength);
            ByteBuffer swappedIpHeader = swapSrcDstAddress(getIpHeader(packet)[0]);
            ByteBuffer swappedTcpHeader = swapTCPSrcDst(getTCPHeader(getIpHeader(packet)[1])[0]);
    
            finalPacket.put(swappedIpHeader.array());
            finalPacket.put(swappedTcpHeader.array());
            finalPacket.put(serverResponseData.array());
    
            Packet finPack = debugPacket(finalPacket);
            Log.e("VPN", "Final packet --> Packet size: " + finPack.getTotalLength() + " from " + finPack.getSourceIp() + " src port: " + finPack.getSrcPort() + " going to " + finPack.getDestinationIp() + " dst port: " + finPack.getDstPort());
    
            out.write(finalPacket.array());
        }
    }catch (Exception e){
        //Log.e(TAG, "EXCEPTION: " + e);
        e.printStackTrace();
    }
    

    这段代码似乎非常缓慢或根本不起作用。有时如果我去www.google.com它会缓慢加载,但大部分时间它都没有。有时我在行int responseLength = socketInputStream.read(serverResponse.array());

    上收到以下错误
      

    java.net.SocketException:recvfrom failed:ECONNRESET(由对等方重置连接)

    导致此错误的原因是什么,我如何正确地将这些数据包转发到适当的目的地?非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

  

导致此错误的原因是什么?

recvfrom failed异常表示服务器已关闭客户端套接字,但客户端仍在读取输入数据(在您的情况下为serverResponse.array()。有关详细信息,请see this

  

如何正确地将这些数据包转发到适当的数据包   目的地?

google-sources here提供了一个转发可用数据包的示例代码。请仔细阅读代码和相关评论。根据谷歌消息来源:

  

此应用程序包含Android客户端和示例   服务器的实现。它执行IP over UDP并且能够执行   只要它在不同网络之间进行无缝切换   接收相同的VPN参数。它显示了如何构建VPN客户端   使用API​​级别14中引入的VpnService类。

     

服务器端实现的示例代码是特定于Linux的   并在server目录中可用。运行服务器或端口   它到另一个平台,请参阅代码中的注释   的信息。

另一个有用的应用链接here