写入 VpnService 输出流没有响应

2024-01-07

我的应用程序实现 VpnService 来拦截网络流量并提供量身定制的响应。目标是处理特定地址的流量,并丢弃其他请求。

目前,我成功解析传入请求并构建和发送响应。然而,问题在于这些响应并不是作为对原始请求的实际响应而到达的。使用套接字连接进行测试只会超时。

为了做出这种区分,我目前正在解析来自 VpnService 输入流的原始 IP 数据包,如下所示:

VpnService.Builder b = new VpnService.Builder();
b.addAddress("10.2.3.4", 28);
b.addRoute("0.0.0.0", 0);
b.setMtu(1500);
...
ParcelFileDescriptor vpnInterface = b.establish();
final FileInputStream in = new FileInputStream(
        vpnInterface.getFileDescriptor());
final FileOutputStream out = new FileOutputStream(
        vpnInterface.getFileDescriptor());

// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(32767);

// We keep forwarding packets till something goes wrong.
try {
    while (vpnInterface != null && vpnInterface.getFileDescriptor() != null
            && vpnInterface.getFileDescriptor().valid()) {
        packet.clear();
        SystemClock.sleep(10);
        // Read the outgoing packet from the input stream.
        final byte[] data = packet.array();
        int length = in.read(data);
        if (length > 0) {
            packet.limit(length);
        /*
         1. Parse the TCP/UDP header
         2. Create an own socket with the same src/dest port/ip
         3. Use protect() on this socket so it not routed over tun0
         4. Send the packet body (excluding the header)
         5. Obtain the response
         6. Add the TCP header to the response and forward it
         */
        final IpDatagram ip = IpDatagram.create(packet);
        ...
    }
}

IpDatagram 是一个类,通过它create()将字节数组解析为 IP 数据包的表示形式,其中包含 IP 标头、选项和正文。我继续根据协议类型解析主体的字节数组。在本例中,我只对带有 TCP 有效负载的 IPv4 感兴趣 - 这里我也创建了 TCP 标头、选项和正文的表示。

获得 IpDatagram 实例后,我可以确定源和目标 IP(从 IP 标头)和端口(从 TCP 标头)。我还确认请求 TCP 的标志(例如 SYN、ACK 和 PSH)和序列号。在应用程序中:

随后,我构造一个新的 IpDatagram 作为响应,其中:

  • 源和目标IP与传入请求相反;
  • 源端口和目标端口与传入请求相反;
  • TCP 确认号设置为传入请求的序列号;
  • 提供虚拟 HTTP/1.1 有效负载作为 TCP 主体。

我将生成的 IpDatagram 转换为字节数组并将其写入 VpnServer 的输出流:

TcpDatagram tcp = new TcpDatagram(tcpHeader, tcpOptions, tcpBody);
IpDatagram ip = new Ip4Datagram(ipHeader, ipOptions, tcp);
out.write(ip.toBytes());

我的应用程序按应有的方式显示传出数据报,但尽管如此,所有连接仍然超时。

以下是十六进制传入 TCP/IP 数据包的示例:

4500003c7de04000400605f10a0203044faa5a3bb9240050858bc52b00000000a00239089a570000020405b40402080a00bfb8cb0000000001030306

以及生成的十六进制传出 TCP/IP 数据包:

450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b501820001fab0000485454502f312e3120323030204f4b0a446174653a2054 75652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e742d547970653a20746578742f68746d6c0a436f6e74656e742d4 c656e6774683a2031320a457870697265733a205475652c203139204e6f7620323031332031323a32333a303320474d540a0a48656c6c6f20776f726c64 21

然而,简单的测试就会超时;我创建了一个新套接字并将其连接到上面的 IP,但上面提供的响应从未到达。

可能出了什么问题?有什么方法可以解决为什么我的回复未送达的问题吗?


此 TCP/IP 响应不包含有效的 TCP 标头校验和:

450000bb30394000800613194faa5a3b0a0203040050b92400a00000858bc52b501820001fab0000485454502f312e3120323030204f4b0a446174653a205475652c203139204e6f7620323031332031323a32333a303320474d540a436f6e74656e7 42d547970653a20746578742f68746d6c0a436f6e74656e742d4c656e6774683a2031320a457870697265733a205475652c203139204e6f762032303133 2031323a32333a303320474d540a0a48656c6c6f20776f726c6421

更一般地说,请求和响应机制非常挑剔。当然,由于网络的本质,情况就是如此,并且由于内核负责确保响应良好以及响应应发送到哪个端口,因此任何不计算的内容都将作为坏数据包被丢弃。当您在网络层上操作时,从 VpnService 的输出流进行响应时也是如此。

回到上面的具体情况:IP 数据包是正确的(包括校验和),但 TCP 数据包不正确。您不仅需要计算 TCP 数据包的 TCP 标头校验和,还需要计算伪标头前缀,如下所示:

TCP pseudo-header
(source: tcpipguide.com http://www.tcpipguide.com/free/diagrams/tcppseudoheader.png)

然后应该通过以下字节进行计算:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

写入 VpnService 输出流没有响应 的相关文章

随机推荐