Protect a socket in VpnService
You need to bind your socket before protecting. This works for me:
Socket socket = new Socket();
//bind to any address
socket.bind(new InetSocketAddress(0));
vpnService.protect(socket);
socket.connect(...);
I found that an alternative solution was to write it out in 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;
}
}
You could exclude your application socket(s) (and thus the traffic flowing past it) from using the VPN by adding your application to vpnService.builder.addDisallowedApplication("your package name")
I tried this and tested it with running tcpdump
on both the vpn tunneled interface and my outgoing Internet interface. The packets from my applications don't loop around in the vpn interface and are sent over the forward facing Internet interface of the phone.
The following code works.
Socket socket = SocketChannel.open().socket();
if ((null != socket) && (null != vpnService)) {
vpnService.protect(socket);
}
socket.connect(...);
new Socket() doesn't have a valid file descriptor, so it cannot be protected.