feature/app-split-tunneling (#702)
App Split Tunneling for Windows and Android
This commit is contained in:
parent
e7bd24f065
commit
adab30fc81
48 changed files with 1225 additions and 98 deletions
|
|
@ -2,6 +2,9 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QQmlFile>
|
||||
#include <QEventLoop>
|
||||
#include <QImage>
|
||||
|
||||
#include <android/bitmap.h>
|
||||
|
||||
#include "android_controller.h"
|
||||
#include "android_utils.h"
|
||||
|
|
@ -214,6 +217,46 @@ void AndroidController::minimizeApp()
|
|||
callActivityMethod("minimizeApp", "()V");
|
||||
}
|
||||
|
||||
QJsonArray AndroidController::getAppList()
|
||||
{
|
||||
QJniObject appList = callActivityMethod<jstring>("getAppList", "()Ljava/lang/String;");
|
||||
QJsonArray jsonAppList = QJsonDocument::fromJson(appList.toString().toUtf8()).array();
|
||||
return jsonAppList;
|
||||
}
|
||||
|
||||
QPixmap AndroidController::getAppIcon(const QString &package, QSize *size, const QSize &requestedSize)
|
||||
{
|
||||
QJniObject bitmap = callActivityMethod<jobject>("getAppIcon", "(Ljava/lang/String;II)Landroid/graphics/Bitmap;",
|
||||
QJniObject::fromString(package).object<jstring>(),
|
||||
requestedSize.width(), requestedSize.height());
|
||||
|
||||
QJniEnvironment env;
|
||||
AndroidBitmapInfo info;
|
||||
if (AndroidBitmap_getInfo(env.jniEnv(), bitmap.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
void *pixels;
|
||||
if (AndroidBitmap_lockPixels(env.jniEnv(), bitmap.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
int width = info.width;
|
||||
int height = info.height;
|
||||
|
||||
size->setWidth(width);
|
||||
size->setHeight(height);
|
||||
|
||||
QImage image(width, height, QImage::Format_RGBA8888);
|
||||
if (info.stride == uint32_t(image.bytesPerLine())) {
|
||||
memcpy((void *) image.constBits(), pixels, info.stride * height);
|
||||
} else {
|
||||
auto *bmpPtr = static_cast<uchar *>(pixels);
|
||||
for (int i = 0; i < height; i++, bmpPtr += info.stride)
|
||||
memcpy((void *) image.constScanLine(i), bmpPtr, width);
|
||||
}
|
||||
|
||||
if (AndroidBitmap_unlockPixels(env.jniEnv(), bitmap.object()) != ANDROID_BITMAP_RESULT_SUCCESS) return {};
|
||||
|
||||
return QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
// Moving log processing to the Android side
|
||||
jclass AndroidController::log;
|
||||
jmethodID AndroidController::logDebug;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define ANDROID_CONTROLLER_H
|
||||
|
||||
#include <QJniObject>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "protocols/vpnprotocol.h"
|
||||
|
||||
|
|
@ -41,6 +42,8 @@ public:
|
|||
void clearLogs();
|
||||
void setScreenshotsEnabled(bool enabled);
|
||||
void minimizeApp();
|
||||
QJsonArray getAppList();
|
||||
QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize);
|
||||
|
||||
static bool initLogging();
|
||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "dnsutilswindows.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "core/networkUtilities.h"
|
||||
#include "platforms/windows/windowscommons.h"
|
||||
#include "platforms/windows/windowsservicemanager.h"
|
||||
#include "windowsfirewall.h"
|
||||
|
|
@ -43,11 +44,24 @@ WindowsDaemon::~WindowsDaemon() {
|
|||
logger.debug() << "Daemon released";
|
||||
}
|
||||
|
||||
void WindowsDaemon::prepareActivation(const InterfaceConfig& config) {
|
||||
void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAdapterIndex) {
|
||||
// Before creating the interface we need to check which adapter
|
||||
// routes to the server endpoint
|
||||
auto serveraddr = QHostAddress(config.m_serverIpv4AddrIn);
|
||||
m_inetAdapterIndex = WindowsCommons::AdapterIndexTo(serveraddr);
|
||||
if (inetAdapterIndex == 0) {
|
||||
auto serveraddr = QHostAddress(config.m_serverIpv4AddrIn);
|
||||
m_inetAdapterIndex = NetworkUtilities::AdapterIndexTo(serveraddr);
|
||||
} else {
|
||||
m_inetAdapterIndex = inetAdapterIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||
if (config.m_vpnDisabledApps.length() > 0) {
|
||||
m_splitTunnelManager.start(m_inetAdapterIndex, vpnAdapterIndex);
|
||||
m_splitTunnelManager.setRules(config.m_vpnDisabledApps);
|
||||
} else {
|
||||
m_splitTunnelManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
||||
|
|
@ -64,12 +78,8 @@ bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
|||
}
|
||||
}
|
||||
|
||||
if (config.m_vpnDisabledApps.length() > 0) {
|
||||
m_splitTunnelManager.start(m_inetAdapterIndex);
|
||||
m_splitTunnelManager.setRules(config.m_vpnDisabledApps);
|
||||
} else {
|
||||
m_splitTunnelManager.stop();
|
||||
}
|
||||
activateSplitTunnel(config);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ class WindowsDaemon final : public Daemon {
|
|||
WindowsDaemon();
|
||||
~WindowsDaemon();
|
||||
|
||||
void prepareActivation(const InterfaceConfig& config) override;
|
||||
void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) override;
|
||||
void activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) override;
|
||||
|
||||
protected:
|
||||
bool run(Op op, const InterfaceConfig& config) override;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ void WindowsSplitTunnel::setRules(const QStringList& appPaths) {
|
|||
logger.debug() << "New Configuration applied: " << getState();
|
||||
}
|
||||
|
||||
void WindowsSplitTunnel::start(int inetAdapterIndex) {
|
||||
void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||
// To Start we need to send 2 things:
|
||||
// Network info (what is vpn what is network)
|
||||
logger.debug() << "Starting SplitTunnel";
|
||||
|
|
@ -171,7 +171,7 @@ void WindowsSplitTunnel::start(int inetAdapterIndex) {
|
|||
}
|
||||
logger.debug() << "Driver is ready || new State:" << getState();
|
||||
|
||||
auto config = generateIPConfiguration(inetAdapterIndex);
|
||||
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
||||
auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
||||
(DWORD)config.size(), nullptr, 0, &bytesReturned,
|
||||
nullptr);
|
||||
|
|
@ -270,14 +270,19 @@ std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
|||
}
|
||||
|
||||
std::vector<uint8_t> WindowsSplitTunnel::generateIPConfiguration(
|
||||
int inetAdapterIndex) {
|
||||
int inetAdapterIndex, int vpnAdapterIndex) {
|
||||
std::vector<uint8_t> out(sizeof(IP_ADDRESSES_CONFIG));
|
||||
|
||||
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
||||
|
||||
auto ifaces = QNetworkInterface::allInterfaces();
|
||||
|
||||
if (vpnAdapterIndex == 0) {
|
||||
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
||||
}
|
||||
|
||||
// Always the VPN
|
||||
getAddress(WindowsCommons::VPNAdapterIndex(), &config->TunnelIpv4,
|
||||
getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
||||
&config->TunnelIpv6);
|
||||
// 2nd best route
|
||||
getAddress(inetAdapterIndex, &config->InternetIpv4, &config->InternetIpv6);
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ class WindowsSplitTunnel final : public QObject {
|
|||
void setRules(const QStringList& appPaths);
|
||||
|
||||
// Fetches and Pushed needed info to move to engaged mode
|
||||
void start(int inetAdapterIndex);
|
||||
void start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||
// Deletes Rules and puts the driver into passive mode
|
||||
void stop();
|
||||
// Resets the Whole Driver
|
||||
|
|
@ -164,7 +164,7 @@ class WindowsSplitTunnel final : public QObject {
|
|||
// Generates a Configuration for Each APP
|
||||
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
||||
// Generates a Configuration which IP's are VPN and which network
|
||||
std::vector<uint8_t> generateIPConfiguration(int inetAdapterIndex);
|
||||
std::vector<uint8_t> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||
std::vector<uint8_t> generateProcessBlob();
|
||||
|
||||
void getAddress(int adapterIndex, IN_ADDR* out_ipv4, IN6_ADDR* out_ipv6);
|
||||
|
|
|
|||
|
|
@ -88,24 +88,6 @@ QString WindowsCommons::tunnelLogFile() {
|
|||
return QString();
|
||||
}
|
||||
|
||||
// static
|
||||
int WindowsCommons::AdapterIndexTo(const QHostAddress& dst) {
|
||||
logger.debug() << "Getting Current Internet Adapter that routes to"
|
||||
<< logger.sensitive(dst.toString());
|
||||
quint32_be ipBigEndian;
|
||||
quint32 ip = dst.toIPv4Address();
|
||||
qToBigEndian(ip, &ipBigEndian);
|
||||
_MIB_IPFORWARDROW routeInfo;
|
||||
auto result = GetBestRoute(ipBigEndian, 0, &routeInfo);
|
||||
if (result != NO_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
auto adapter =
|
||||
QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex);
|
||||
logger.debug() << "Internet Adapter:" << adapter.name();
|
||||
return routeInfo.dwForwardIfIndex;
|
||||
}
|
||||
|
||||
// static
|
||||
int WindowsCommons::VPNAdapterIndex() {
|
||||
// For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:(
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ class WindowsCommons final {
|
|||
|
||||
// Returns the Interface Index of the VPN Adapter
|
||||
static int VPNAdapterIndex();
|
||||
// Returns the Interface Index that could Route to dst
|
||||
static int AdapterIndexTo(const QHostAddress& dst);
|
||||
|
||||
// Returns the Path of the Current process
|
||||
static QString getCurrentPath();
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue