Android 的防火墙与 VpnService。已传送响应,但抛出 SocketTimeoutException
A firewall for Android with VpnService. Responses are delivered, but a SocketTimeoutException is thrown
我正在使用 VpnService. My app is similar to ToyVpnService 为 Android 实施一个简单的防火墙,但它不会将原始 IP 数据包发送到远程 VPN 服务器,后者会将它们转发到它们的目的地。
我可以在本地完成所有这些转发程序吗?这就是我要实现的。
我初始化一个 TUN 设备及其文件描述符:
mInterface = new Builder().setSession(getString(R.string.app_name))
.addAddress("10.0.2.1", 24)
.addRoute("0.0.0.0", 1)
.addRoute("128.0.0.0", 1)
.establish();
in = new FileInputStream(mInterface.getFileDescriptor());
out = new FileOutputStream(mInterface.getFileDescriptor());
我将 0.0.0.0/1 和 128.0.0.0/1 分配给 TUN 设备,使其比默认路由 0.0.0.0/0 更可取。我在下面的相同异常中使用了 0.0.0.0/0 和 运行。
这是一个 UDP 请求示例。
1).我从TUN设备读取了一个IP包。
05-06 00:46:52.749: D/UDPChecksum(31077): Sent == [69, 0, 0, 36, 0, 0, 64, 0, 64, 17, 108, 91, 10, 0, 2, 1, -64, -88, 1, -59, -53, 1, -50, -87, 0, 16, 89, -114, 85, 68, 80, 95, 68, 65, 84, 65]
请考虑IPv4数据包结构here.
例如第一个数字69(二进制为0100 0101)表示IP协议的版本为4(4high-order位)。而4个low-order位代表32位字中的Internet Header Length (IHL)。
2). 然后创建一个 protected DatagramSocket
并发送数据 (without 它的 IP和 UDP headers) 到我从捕获的 IP 数据包中读取的目标地址。
3). 我收到来自远程机器的响应,想将其发送回初始化请求的应用程序。
4).我交换IP包中的源IP地址和目的IP地址和端口号,计算IPv4 header校验和UDP校验和(已经构造一个 IPv4 伪 header).
05-06 00:46:52.889: D/UDPChecksum(31077): mIpv4PseudoHeader == [-64, -88, 1, -59, 10, 0, 2, 1, 0, 17, 0, 14]
5). 然后我将计算出的校验和设置到IP数据包的相应索引并将IP数据包写入out
,TUN的输出流设备。
05-06 00:46:52.889: D/UDPChecksum(31077): To TUN == [69, 0, 0, 34, 0, 0, 64, 0, 64, 17, 108, 93, -64, -88, 1, -59, 10, 0, 2, 1, -50, -87, -53, 1, 0, 14, -105, -72, 85, 68, 80, 95, 79, 75]
响应到达我的应用程序。在调用其 receive() 方法后被阻塞的 DatagramSocket
填充了我提供的缓冲区。
byte[] responseBuffer = new byte[RESPONSE_SIZE];
try {
mDatagramSocket.send(mDatagramPacket);
final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length);
mDatagramSocket.receive(response);
} catch (IOException e) {
Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here.
logException(e);
}
但是它的套接字在超过超时时会抛出异常。
05-05 23:46:58.389: E/CLIENT(20553): java.net.SocketTimeoutException
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:551)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:509)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.DatagramSocket.receive(DatagramSocket.java:250)
05-05 23:46:58.389: E/CLIENT(20553): at socket.client.MainActivity$UdpThread.run(MainActivity.java:195)
05-05 23:46:58.389: E/CLIENT(20553): Caused by: libcore.io.ErrnoException: recvfrom failed: EAGAIN (Try again)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfromBytes(Native Method)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfrom(Posix.java:141)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
05-05 23:46:58.389: E/CLIENT(20553): ... 4 more
没有防火墙一切正常。
我阅读了这些讨论,但没有帮助:
Android firewall with VpnService
尝试添加缺少的 SocketTimeoutException 异常处理程序
byte[] responseBuffer = new byte[RESPONSE_SIZE];
try {
mDatagramSocket.send(mDatagramPacket);
final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length);
mDatagramSocket.receive(response);
} catch (SocketTimeoutException e) {
// ignore
; // continue;
} catch (IOException e) {
Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here.
logException(e);
}
来自 recvfrom() 调用文档:
[EAGAIN] or [EWOULDBLOCK]
The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is
available and either the socket's file descriptor is marked O_NONBLOCK
or the socket does not support blocking to await out-of-band data.
简化说明
If no messages are available at the socket, the receive calls wait for
a message to arrive, unless the socket is nonblocking (see fcntl(2)),
in which case the value -1 is returned and the external variable errno
set to EAGAIN
DatagramSocket 未处于阻塞模式,看起来您正在尝试从套接字读取数据,但没有数据可读,您确定您确实在接收数据吗?尝试为每个接收到的数据包清理缓冲区。
重点是我使用了错误的 IPv4 伪 header 来计算校验和。它既不包含数据包 UDP header 也不包含传输的数据。由于我包含了 UDP header 和数据,所以我没有看到原始问题的异常。
我正在使用 VpnService. My app is similar to ToyVpnService 为 Android 实施一个简单的防火墙,但它不会将原始 IP 数据包发送到远程 VPN 服务器,后者会将它们转发到它们的目的地。
我可以在本地完成所有这些转发程序吗?这就是我要实现的。
我初始化一个 TUN 设备及其文件描述符:
mInterface = new Builder().setSession(getString(R.string.app_name))
.addAddress("10.0.2.1", 24)
.addRoute("0.0.0.0", 1)
.addRoute("128.0.0.0", 1)
.establish();
in = new FileInputStream(mInterface.getFileDescriptor());
out = new FileOutputStream(mInterface.getFileDescriptor());
我将 0.0.0.0/1 和 128.0.0.0/1 分配给 TUN 设备,使其比默认路由 0.0.0.0/0 更可取。我在下面的相同异常中使用了 0.0.0.0/0 和 运行。
这是一个 UDP 请求示例。
1).我从TUN设备读取了一个IP包。
05-06 00:46:52.749: D/UDPChecksum(31077): Sent == [69, 0, 0, 36, 0, 0, 64, 0, 64, 17, 108, 91, 10, 0, 2, 1, -64, -88, 1, -59, -53, 1, -50, -87, 0, 16, 89, -114, 85, 68, 80, 95, 68, 65, 84, 65]
请考虑IPv4数据包结构here. 例如第一个数字69(二进制为0100 0101)表示IP协议的版本为4(4high-order位)。而4个low-order位代表32位字中的Internet Header Length (IHL)。
2). 然后创建一个 protected DatagramSocket
并发送数据 (without 它的 IP和 UDP headers) 到我从捕获的 IP 数据包中读取的目标地址。
3). 我收到来自远程机器的响应,想将其发送回初始化请求的应用程序。
4).我交换IP包中的源IP地址和目的IP地址和端口号,计算IPv4 header校验和UDP校验和(已经构造一个 IPv4 伪 header).
05-06 00:46:52.889: D/UDPChecksum(31077): mIpv4PseudoHeader == [-64, -88, 1, -59, 10, 0, 2, 1, 0, 17, 0, 14]
5). 然后我将计算出的校验和设置到IP数据包的相应索引并将IP数据包写入out
,TUN的输出流设备。
05-06 00:46:52.889: D/UDPChecksum(31077): To TUN == [69, 0, 0, 34, 0, 0, 64, 0, 64, 17, 108, 93, -64, -88, 1, -59, 10, 0, 2, 1, -50, -87, -53, 1, 0, 14, -105, -72, 85, 68, 80, 95, 79, 75]
响应到达我的应用程序。在调用其 receive() 方法后被阻塞的 DatagramSocket
填充了我提供的缓冲区。
byte[] responseBuffer = new byte[RESPONSE_SIZE];
try {
mDatagramSocket.send(mDatagramPacket);
final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length);
mDatagramSocket.receive(response);
} catch (IOException e) {
Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here.
logException(e);
}
但是它的套接字在超过超时时会抛出异常。
05-05 23:46:58.389: E/CLIENT(20553): java.net.SocketTimeoutException
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:551)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:509)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169)
05-05 23:46:58.389: E/CLIENT(20553): at java.net.DatagramSocket.receive(DatagramSocket.java:250)
05-05 23:46:58.389: E/CLIENT(20553): at socket.client.MainActivity$UdpThread.run(MainActivity.java:195)
05-05 23:46:58.389: E/CLIENT(20553): Caused by: libcore.io.ErrnoException: recvfrom failed: EAGAIN (Try again)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfromBytes(Native Method)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.Posix.recvfrom(Posix.java:141)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
05-05 23:46:58.389: E/CLIENT(20553): at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
05-05 23:46:58.389: E/CLIENT(20553): ... 4 more
没有防火墙一切正常。 我阅读了这些讨论,但没有帮助:
Android firewall with VpnService
尝试添加缺少的 SocketTimeoutException 异常处理程序
byte[] responseBuffer = new byte[RESPONSE_SIZE];
try {
mDatagramSocket.send(mDatagramPacket);
final DatagramPacket response = new DatagramPacket(responseBuffer, responseBuffer.length);
mDatagramSocket.receive(response);
} catch (SocketTimeoutException e) {
// ignore
; // continue;
} catch (IOException e) {
Log.e("NoRootFwService", "error: " + Arrays.toString(responseBuffer)); // I can see the correct response here.
logException(e);
}
来自 recvfrom() 调用文档:
[EAGAIN] or [EWOULDBLOCK] The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is available and either the socket's file descriptor is marked O_NONBLOCK or the socket does not support blocking to await out-of-band data.
简化说明
If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno set to EAGAIN
DatagramSocket 未处于阻塞模式,看起来您正在尝试从套接字读取数据,但没有数据可读,您确定您确实在接收数据吗?尝试为每个接收到的数据包清理缓冲区。
重点是我使用了错误的 IPv4 伪 header 来计算校验和。它既不包含数据包 UDP header 也不包含传输的数据。由于我包含了 UDP header 和数据,所以我没有看到原始问题的异常。