Защитите сокет в VpnService
Я изучаю возможности Android VpnService. В настоящее время я создал очень рудиментарный запрос-экспедитор, по существу перестраивая IP-стек в пространстве пользователя: я читаю IP-пакеты из входного потока VpnService, разбираю их и для соединений, которые я не хочу пересылать, я пытаюсь воссоздать те сокетов за пределами VPN-соединения.
Я понял, что этот последний бит облегчается 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.
В то время как вышеприведенный код просто блокирует и, в конечном итоге, время ожидания, я наблюдаю loopback, вызывая Socket.connect(InetSocketAddress)
из отдельного потока; соединение возвращается прямо в поток ввода VpnService, и процесс повторяется.
Излишне говорить, что это вызывает цикл. У меня возникает ощущение, что причина этого в том, что во время создания сокета (а затем и на вызов VpnService.protect(Socket)
) я еще не установил IP-адрес и порт назначения.
Это действительно так, потому что, переопределяя VpnService.protect(Socket)
и VpnService.protect(int)
в моей реализации VpnService и вызывая supers в обоих случаях, возвращает false.
Как правильно защитить соединение сокета?
Ответы
Ответ 1
Работает следующий код.
Socket socket = SocketChannel.open().socket();
if ((null != socket) && (null != vpnService)) {
vpnService.protect(socket);
}
socket.connect(...);
новый Socket() не имеет допустимого дескриптора файла, поэтому он не может быть защищен.
Ответ 2
Я обнаружил, что альтернативным решением было написать его в C/С++.
Java:
public native int createSocket();
public native int connectSocket(int fd);
С++:
// 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;
}
}
Ответ 3
Перед защитой необходимо связать сокет. Это работает для меня:
Socket socket = new Socket();
//bind to any address
socket.bind(new InetSocketAddress(0));
vpnService.protect(socket);
socket.connect(...);
Ответ 4
Вы можете исключить ваш сокет приложения (и, следовательно, трафик, проходящий мимо него), используя VPN, добавив ваше приложение в vpnService.builder.addDisallowedApplication("your package name")
Я попробовал это и протестировал его с запуском tcpdump
как на туннельном интерфейсе vpn, так и на моем исходящем интернет-интерфейсе. Пакеты из моих приложений не пересекаются в интерфейсе vpn и отправляются через интерфейс интернет-интерфейса, обращенный к Интернету.