保护VpnService中的套接字

我正在探索Android的VpnService的function。 目前,我已经基本上重build了用户空间中的IP栈,构build了一个非常基本的请求转发器:我从VpnService的inputstream中读取IP数据包,parsing它们,并且为了不想转发的连接,我尝试重新创buildVPN连接之外套接字连接。

我已经明白,这最后一点是由VpnService.protect()促进,并试图执行它如下:

 Socket socket = new Socket(); vpnService.protect(socket); socket.connect(new InetSocketAddress( header.getDestinationAddress(), // From my IP datagram header body.getDestinationPort())); // From the TCP datagram header 

不幸的是,这种方法导致了VPN接口的环回。

而上面的代码将会阻塞并最终超时,我通过从一个单独的线程调用Socket.connect(InetSocketAddress)来观察环回; 连接直接回到我的VpnService的inputstream中,并重复这个过程。

不用说,这会导致一个循环。 我感觉这是因为在套接字创build时(以及对VpnService.protect(Socket)的调用),我还没有设置目的IP和端口。

这似乎确实如此,因为在我的VpnService实现中重写VpnService.protect(Socket)VpnService.protect(int)并在这两种情况下调用supers都返回false。

我怎样才能妥善保护套接字连接?

Solutions Collecting From Web of "保护VpnService中的套接字"

以下代码工作。

 Socket socket = SocketChannel.open().socket(); if ((null != socket) && (null != vpnService)) { vpnService.protect(socket); } socket.connect(...); 

新的Socket()没有一个有效的文件描述符,所以它不能被保护。

我发现另一种解决scheme是用C / C ++写出来。

Java的:

 public native int createSocket(); public native int connectSocket(int fd); 

C ++:

 // For sockets #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> // For error codes #include <errno.h> extern "C" { JNIEXPORT jint JNICALL Java_com_pixplicity_example_jni_VpnInterface_createSocket( JNIEnv * env, jobject thiz) { // Create the socket int sockfd = socket(AF_INET, SOCK_STREAM, 0); int err = errno; // Return the file descriptor return sockfd; } JNIEXPORT jint JNICALL Java_com_pixplicity_example_jni_VpnInterface_connectSocket( JNIEnv * env, jobject thiz, jint sockFd) { // Host & port are hard-coded here char* host = "74.125.136.113"; // google.com int port = 80; struct sockaddr_in peerAddr; int ret; peerAddr.sin_family = AF_INET; peerAddr.sin_port = htons(port); peerAddr.sin_addr.s_addr = inet_addr(host); // Connect to host ret = connect((int) sockFd, (struct sockaddr *) &peerAddr, sizeof(peerAddr)); if (ret != 0) { perror("connect failed"); close(sockFd); } // Return the error code return ret; } } 

您需要在保护之前绑定您的套接字。 这适用于我:

 Socket socket = new Socket(); //bind to any address socket.bind(new InetSocketAddress(0)); vpnService.protect(socket); socket.connect(...); 

您可以通过将您的应用程序添加到vpnService.builder.addDisallowedApplication("your package name")来排除您的应用程序套接字(以及stream经它的stream量vpnService.builder.addDisallowedApplication("your package name")

我试过这个,并在vpn隧道接口和我的出口Internet接口上运行tcpdump来testing它。 来自我的应用程序的数据包不会在vpn接口中循环,并通过电话的面向前的Internet接口发送。