java - Android VpnService 转发数据包

标签 java android sockets tcp packet-capture

我想使用 android 的 VpnService 来捕获数据包并根据 IP 地址过滤它们。我可以很好地从“tun”接口(interface)获取数据包,但在那之后我不确定如何将它们转发到它们的原始目的地。基于 this answer 的评论看来我只需要:

  1. 创建到目标 IP 地址和端口的新套接字
  2. 修剪 IP 和 TCP header 以仅发送数据
  3. 收到响应后重新附加 IP 和 TCP header
  4. 将完整的数据包发送到输出流

我试过这样发送数据:

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 (Connection reset by peer)

是什么导致了这个错误,我怎样才能正确地将这些数据包转发到适当的目的地?非常感谢任何帮助!

最佳答案

What is causing this error?

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

how can i properly forward these packets to the appropriate destination?

google-sources here 中有一个示例代码可以转发可用的数据包。请仔细阅读代码和相关评论。根据谷歌资源:

This application consists of an Android client and a sample implementation of a server. It performs IP over UDP and is capable of doing seamless handover between different networks as long as it receives the same VPN parameters.It shows how to build a VPN client using the VpnService class introduced in API level 14.

The sample code of the server-side implementation is Linux-specific and is available in the server directory. To run the server or port it to another platform, please see comments in the code for the details.

另一个有用的应用程序链接 here

关于java - Android VpnService 转发数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37267110/

相关文章:

android - 使用微调器和 AlertDialog 更新 ListView

c - 第二次 recv 调用不接收数据,在 C 中停止执行

c - 使用 C 中的套接字通过多个客户端传输文件时出现格式错误

ios - Udp套接字绑定(bind)失败

java - 需要根据选择的TAB显示 Activity

android - 改造上传将表单数据存储在上传文件中,从而损坏它

java - 当我要求它在 toast 中显示保存的数据时,我的应用程序崩溃了 - 我

java - 安卓,netcat : how to send more than 2048 Byte over UDP

java - 如何从文本字段获取 Long 值并将表单发送到 ArrayList

java - 与系统时间比较后程序无限期执行