我想使用android的VpnService来捕获数据包,根据IP地址过滤它们。我可以很好地从“tun”接口获取数据包,但之后我不知道如何将它们转发到它们的原始目的地。根据this answer的评论,我似乎只需要:
为目标IP地址和端口创建新的套接字
修剪IP和TCP头以仅发送数据
收到响应时重新连接IP和TCP头
将完整的数据包发送到输出流
我尝试过这样发送数据:

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失败:econnReset(由对等方重置连接)
导致此错误的原因是什么?如何正确地将这些数据包转发到适当的目标?非常感谢您的帮助!

最佳答案

是什么导致了这个错误?
recvfrom failed异常表示服务器已关闭客户端套接字,但客户端仍在读取输入数据(在您的情况下为serverResponse.array()。有关详细信息,see this
我怎样才能把这些包转发给合适的
目的地?
google sourceshere提供了一个示例代码,用于转发可用的数据包。请仔细阅读代码和相关注释。根据谷歌的消息来源:
这个应用程序包括一个android客户端和一个示例
服务器的实现。它通过udp执行ip,并且能够
在不同网络之间进行无缝切换
接收相同的VPN参数。它显示如何构建VPN客户端。
使用API级别14中引入的vService类。
服务器端实现的示例代码是特定于Linux的
可在server目录中找到。运行服务器或端口
它到另一个平台,请参见代码中的注释
细节。
另一个有用的应用程序链接here

07-25 23:59
查看更多