Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tor over VPN functionality #1512

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions main/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,16 @@ dependencies {
val coreVersion = "1.7.0"
val materialVersion = "1.5.0"
val fragment_version = "1.4.1"
val ipt_proxy = "1.7.1"
val pcap_core = "1.8.2"
val pcap_factory = "1.8.2"


implementation("androidx.annotation:annotation:1.3.0")
implementation("androidx.core:core:$coreVersion")
implementation("com.github.bitmold:OrbotIPtProxy:$ipt_proxy")
implementation("org.pcap4j:pcap4j-core:$pcap_core")
implementation("org.pcap4j:pcap4j-packetfactory-static:$pcap_factory")


// Is there a nicer way to do this?
Expand Down
3 changes: 3 additions & 0 deletions main/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ target_link_libraries(ovpnutil log)
add_library(rsapss SHARED ovpnutil/rsapss.cpp)
target_link_libraries(rsapss log crypto ssl)

add_library(torproxy SHARED torproxy/torproxy.cpp)
target_link_libraries(torproxy log)

if (NOT ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} MATCHES "build/intermediates/cmake/.*skeleton.*/")
add_library(osslspeedtest SHARED ovpnutil/sslspeed.c)
target_link_libraries(osslspeedtest log crypto ssl)
Expand Down
124 changes: 124 additions & 0 deletions main/src/main/cpp/torproxy/torproxy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// Created by pinkolik on 7/27/22.
//
//https://www.dan.me.uk/torlist/
#include <sys/socket.h>
#include <android/log.h>
#include <pthread.h>
#include <unistd.h>
#include "torproxy.h"
#include <iostream>
#include <set>

using namespace std;

set<int> g_tor_nodes_hash;
int g_is_running;


int calc_hashcode_of_ip(char *ip) {
int result = 0;
for (int i = 0; i < 4; i++) {
result = 337 * result + ip[i];
}
return result;
}

extern "C" void Java_de_blinkt_openvpn_core_TorProxy_initTorPublicNodes(JNIEnv *env, jclass clazz,
jobjectArray nodes_addresses) {
g_tor_nodes_hash.clear();

jsize len = env->GetArrayLength(nodes_addresses);

for (int i = 0; i < len; i++) {
jbyteArray arr = (jbyteArray) env->GetObjectArrayElement(nodes_addresses, i);
jbyte *vals = env->GetByteArrayElements(arr, nullptr);
int hash = calc_hashcode_of_ip((char *) vals);
g_tor_nodes_hash.insert(hash);
env->ReleaseByteArrayElements(arr, vals, 0);
}
}

extern "C" void
Java_de_blinkt_openvpn_core_TorProxy_processIncomingPackets(JNIEnv *env, jclass clazz, jint in_fd,
jint out_fd) {
char buf[16384];
size_t len = sizeof(buf);
int max_fd = out_fd + 1;
fd_set current_sockets, ready_sockets;
FD_ZERO(&current_sockets);
FD_SET(out_fd, &current_sockets);

//1 second timeout
struct timeval timeout = {1, 0};

while (g_is_running != -1) {
ready_sockets = current_sockets;

select(max_fd, &ready_sockets, nullptr, nullptr, &timeout);
if (FD_ISSET(out_fd, &ready_sockets)) {
int n = read(out_fd, buf, len);
if (n > 0) {
int src_addr_hash = calc_hashcode_of_ip(buf + SRC_ADDR_OFFSET);
auto it = g_tor_nodes_hash.find(src_addr_hash);
if (it != g_tor_nodes_hash.end()) {
//Tor Packet
write(in_fd, buf, n);
}
}
}
}
close(out_fd);
}

extern "C" void
Java_de_blinkt_openvpn_core_TorProxy_processOutgoingPackets(JNIEnv *env, jclass clazz, jint in_fd,
jint out_fd) {
char buf[16384];
size_t len = sizeof(buf);
int max_fd = in_fd + 1;
fd_set current_sockets, ready_sockets;
FD_ZERO(&current_sockets);
FD_SET(in_fd, &current_sockets);

jmethodID jMethodId = env->GetStaticMethodID(clazz, "sendPacketsToTor", "([B)V");

//1 second timeout
struct timeval timeout = {1, 0};

while (g_is_running != -1) {
ready_sockets = current_sockets;

select(max_fd, &ready_sockets, nullptr, nullptr, &timeout);
if (FD_ISSET(in_fd, &ready_sockets)) {
int n = read(in_fd, buf, len);
if (n > 0) {
int dst_addr_hash = calc_hashcode_of_ip(buf + DST_ADDR_OFFSET);
auto it = g_tor_nodes_hash.find(dst_addr_hash);
if (it != g_tor_nodes_hash.end()) {
//Tor Packet
write(out_fd, buf, n);
} else if (g_is_running == 1) { //Tor Connected
jbyteArray jByteArray = env->NewByteArray(n);
env->SetByteArrayRegion(jByteArray, 0, n, (jbyte *) buf);
env->CallStaticVoidMethod(clazz, jMethodId, jByteArray);
}
}
}
}
close(in_fd);
}

extern "C" void
Java_de_blinkt_openvpn_core_TorProxy_setIsRunning(JNIEnv *env, jclass clazz, jint is_running) {
g_is_running = is_running;
}

extern "C" void
Java_de_blinkt_openvpn_core_TorProxy_writeToDevice(JNIEnv *env, jclass clazz, jbyteArray packet,
jint in_fd) {
jsize len = env->GetArrayLength(packet);
jbyte *vals = env->GetByteArrayElements(packet, nullptr);
write(in_fd, vals, len);
env->ReleaseByteArrayElements(packet, vals, 0);
}
35 changes: 35 additions & 0 deletions main/src/main/cpp/torproxy/torproxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Created by pinkolik on 7/27/22.
//

#include <jni.h>

#ifndef ICS_OPENVPN_TORPROXY_H
#define ICS_OPENVPN_TORPROXY_H


static const int SRC_ADDR_OFFSET = 12;
static const int DST_ADDR_OFFSET = 16;

#endif //ICS_OPENVPN_TORPROXY_H


extern "C"
JNIEXPORT void JNICALL
Java_de_blinkt_openvpn_core_TorProxy_initTorPublicNodes(JNIEnv *env, jclass clazz,
jobjectArray nodes_addresses);
extern "C"
JNIEXPORT void JNICALL
Java_de_blinkt_openvpn_core_TorProxy_processIncomingPackets(JNIEnv *env, jclass clazz, jint in_fd,
jint out_fd);
extern "C"
JNIEXPORT void JNICALL
Java_de_blinkt_openvpn_core_TorProxy_processOutgoingPackets(JNIEnv *env, jclass clazz, jint in_fd,
jint out_fd);
extern "C"
JNIEXPORT void JNICALL
Java_de_blinkt_openvpn_core_TorProxy_setIsRunning(JNIEnv *env, jclass clazz, jint is_running);
extern "C"
JNIEXPORT void JNICALL
Java_de_blinkt_openvpn_core_TorProxy_writeToDevice(JNIEnv *env, jclass clazz, jbyteArray packet,
jint in_fd);
1 change: 1 addition & 0 deletions main/src/main/java/de/blinkt/openvpn/VpnProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ public class VpnProfile implements Serializable, Cloneable {
public int mCompatMode = 0;
public boolean mUseLegacyProvider = false;
public String mTlSCertProfile = "";
public boolean mTorOverVpn = false;

private transient PrivateKey mPrivateKey;
// Public attributes, since I got mad with getter/setter
Expand Down
49 changes: 49 additions & 0 deletions main/src/main/java/de/blinkt/openvpn/core/DNSResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2012-2022 Arne Schwabe
* Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/

package de.blinkt.openvpn.core;

import android.net.TrafficStats;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class DNSResolver {

private final int mPort;
private InetAddress mLocalhost = null;

public DNSResolver(int localPort) {
mPort = localPort;
}

public byte[] processDNS(byte[] payload) throws IOException {
// HACK
// https://stackoverflow.com/questions/47723973/strictmode-java-lang-throwable-untagged-socket-detected
// see also de/blinkt/openvpn/fragments/ImportRemoteConfig.kt:buildHttpClient
int THREAD_ID = 10001;
TrafficStats.setThreadStatsTag(THREAD_ID);
if (mLocalhost == null) {
mLocalhost = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
}

DatagramPacket packet = new DatagramPacket(
payload, payload.length, mLocalhost, mPort
);
try (DatagramSocket datagramSocket = new DatagramSocket()) {
datagramSocket.send(packet);

// Await response from DNS server
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf, buf.length);
datagramSocket.receive(packet);
}

return packet.getData();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1354,5 +1354,10 @@ public void trigger_sso(String info) {
mNotificationManager.notify(notificationId, notification);
}


public boolean isTorOverVpnEnabled() {
if (mProfile == null) {
return false;
}
return mProfile.mTorOverVpn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import android.system.Os;
import android.util.Log;
import de.blinkt.openvpn.R;
Expand Down Expand Up @@ -56,7 +55,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
@Override
public void run() {
sendProxyCMD(Connection.ProxyType.SOCKS5, "127.0.0.1", Integer.toString(OrbotHelper.SOCKS_PROXY_PORT_DEFAULT), false);
OrbotHelper.get(mOpenVPNService).removeStatusCallback(statusCallback);
OrbotHelper.get().removeStatusCallback(statusCallback);

}
};
Expand All @@ -82,7 +81,7 @@ public void onNotYetInstalled() {
public void onOrbotReady(Intent intent, String socksHost, int socksPort) {
mResumeHandler.removeCallbacks(orbotStatusTimeOutRunnable);
sendProxyCMD(Connection.ProxyType.SOCKS5, socksHost, Integer.toString(socksPort), false);
OrbotHelper.get(mOpenVPNService).removeStatusCallback(this);
OrbotHelper.get().removeStatusCallback(this);
}

@Override
Expand Down Expand Up @@ -110,6 +109,11 @@ private static boolean stopOpenVPN() {
} catch (IOException e) {
// Ignore close error on already closed socket
}
}
try {
TorProxy.closeResources();
} catch (Exception ignored) {

}
return sendCMD;
}
Expand Down Expand Up @@ -481,7 +485,7 @@ private void processProxyCMD(String argument) {

if (proxyType == Connection.ProxyType.ORBOT) {
VpnStatus.updateStateString("WAIT_ORBOT", "Waiting for Orbot to start", R.string.state_waitorbot, ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET);
OrbotHelper orbotHelper = OrbotHelper.get(mOpenVPNService);
OrbotHelper orbotHelper = OrbotHelper.get();
if (!OrbotHelper.checkTorReceier(mOpenVPNService))
VpnStatus.logError("Orbot does not seem to be installed!");

Expand Down Expand Up @@ -630,8 +634,20 @@ private boolean sendTunFD(String needed, String extra) {
return false;

Method setInt;
int fdint = pfd.getFd();
boolean torOverVpnEnabled = mOpenVPNService.isTorOverVpnEnabled();
int fdint;
try {
ParcelFileDescriptor proxy = null;
if (!torOverVpnEnabled) {
fdint = pfd.getFd();
Pinkolik marked this conversation as resolved.
Show resolved Hide resolved
} else {
proxy = TorProxy.createProxy(pfd, mOpenVPNService);
FileDescriptor fileDescriptor = proxy.getFileDescriptor();
Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
fdint = (int) getInt.invoke(fileDescriptor);
}


setInt = FileDescriptor.class.getDeclaredMethod("setInt$", int.class);
FileDescriptor fdtosend = new FileDescriptor();

Expand All @@ -649,7 +665,12 @@ private boolean sendTunFD(String needed, String extra) {
// Set the FileDescriptor to null to stop this mad behavior
mSocket.setFileDescriptorsForSend(null);

pfd.close();
if (proxy != null) {
//We don't need to close pfd because we detached it in TorProxy.createProxy
proxy.close();
} else {
pfd.close();
}
Pinkolik marked this conversation as resolved.
Show resolved Hide resolved

return true;
} catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException |
Expand Down
2 changes: 1 addition & 1 deletion main/src/main/java/de/blinkt/openvpn/core/OrbotHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private OrbotHelper() {

}

public static OrbotHelper get(OpenVPNService mOpenVPNService) {
public static OrbotHelper get() {
if (mInstance == null)
mInstance = new OrbotHelper();
return mInstance;
Expand Down
Loading