ShadowSocks fixes for MacOS

This commit is contained in:
pokamest 2021-02-21 09:44:53 -08:00
parent a1cb4ac544
commit 8fd81be477
20 changed files with 583 additions and 482 deletions

View file

@ -56,6 +56,7 @@ enum ErrorCode
// Distro errors
OpenVpnExecutableMissing,
EasyRsaExecutableMissing,
ShadowSocksExecutableMissing,
AmneziaServiceConnectionFailed,
// VPN errors

View file

@ -237,6 +237,9 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
config.replace("$PRIV_KEY", connData.privKey);
config.replace("$TA_KEY", connData.taKey);
#ifdef Q_OS_MAC
config.replace("block-outside-dns", "");
#endif
//qDebug().noquote() << config;
return config;
}

View file

@ -104,12 +104,13 @@ void OpenVpnProtocol::sendManagementCommand(const QString& command)
QIODevice *device = dynamic_cast<QIODevice*>(m_managementServer.socket().data());
if (device) {
QTextStream stream(device);
stream << command << endl;
stream << command << Qt::endl;
}
}
void OpenVpnProtocol::updateRouteGateway(QString line)
{
// TODO: fix for macos
line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1);
if (!line.contains("/")) return;
m_routeGateway = line.split("/", QString::SkipEmptyParts).first();
@ -229,7 +230,6 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
if (line.contains("CONNECTED,SUCCESS")) {
sendByteCount();
stopTimeoutTimer();
updateVpnGateway();
setConnectionState(VpnProtocol::ConnectionState::Connected);
continue;
} else if (line.contains("EXITING,SIGTER")) {
@ -246,6 +246,10 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
updateRouteGateway(line);
}
if (line.contains("PUSH: Received control message")) {
updateVpnGateway(line);
}
if (line.contains("FATAL")) {
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
@ -272,46 +276,61 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
}
}
void OpenVpnProtocol::updateVpnGateway()
void OpenVpnProtocol::updateVpnGateway(const QString &line)
{
QProcess ipconfig;
ipconfig.start("ipconfig", QStringList() << "/all");
ipconfig.waitForStarted();
ipconfig.waitForFinished();
// line looks like
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
QString d = ipconfig.readAll();
d.replace("\r", "");
//qDebug().noquote() << d;
QStringList params = line.split(",");
for (const QString &l : params) {
if (l.contains("ifconfig")) {
if (l.split(" ").size() == 3) {
m_vpnAddress = l.split(" ").at(1);
m_vpnGateway = l.split(" ").at(2);
QStringList adapters = d.split(":\n");
bool isTapV9Present = false;
QString tapV9;
for (int i = 0; i < adapters.size(); ++i) {
if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
isTapV9Present = true;
tapV9 = adapters.at(i);
break;
qDebug() << QString("Set vpn address %1, gw %2").arg(m_vpnAddress).arg(vpnGateway());
}
}
}
if (!isTapV9Present) {
m_vpnGateway = "";
}
QStringList lines = tapV9.split("\n");
for (int i = 0; i < lines.size(); ++i) {
if (!lines.at(i).contains("DHCP")) continue;
// QProcess ipconfig;
// ipconfig.start("ipconfig", QStringList() << "/all");
// ipconfig.waitForStarted();
// ipconfig.waitForFinished();
QRegularExpression re("(: )([\\d\\.]+)($)");
QRegularExpressionMatch match = re.match(lines.at(i));
// QString d = ipconfig.readAll();
// d.replace("\r", "");
// //qDebug().noquote() << d;
if (match.hasMatch()) {
qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
m_vpnGateway = match.captured(2);
return;
}
else continue;
}
// QStringList adapters = d.split(":\n");
m_vpnGateway = "";
// bool isTapV9Present = false;
// QString tapV9;
// for (int i = 0; i < adapters.size(); ++i) {
// if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
// isTapV9Present = true;
// tapV9 = adapters.at(i);
// break;
// }
// }
// if (!isTapV9Present) {
// m_vpnGateway = "";
// }
// QStringList lines = tapV9.split("\n");
// for (int i = 0; i < lines.size(); ++i) {
// if (!lines.at(i).contains("DHCP")) continue;
// QRegularExpression re("(: )([\\d\\.]+)($)");
// QRegularExpressionMatch match = re.match(lines.at(i));
// if (match.hasMatch()) {
// qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
// m_vpnGateway = match.captured(2);
// return;
// }
// else continue;
// }
// m_vpnGateway = "";
}

View file

@ -47,7 +47,7 @@ private:
private:
void updateRouteGateway(QString line);
void updateVpnGateway();
void updateVpnGateway(const QString &line);
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
};

View file

@ -14,6 +14,12 @@ ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration,
readShadowSocksConfiguration(configuration);
}
ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol()
{
qDebug() << "ShadowSocksVpnProtocol::stop()";
ShadowSocksVpnProtocol::stop();
}
ErrorCode ShadowSocksVpnProtocol::start()
{
qDebug() << "ShadowSocksVpnProtocol::start()";
@ -40,7 +46,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
return OpenVpnProtocol::start();
}
else return ErrorCode::FailedToStartRemoteProcessError;
else return ErrorCode::ShadowSocksExecutableMissing;
}
void ShadowSocksVpnProtocol::stop()

View file

@ -8,6 +8,7 @@ class ShadowSocksVpnProtocol : public OpenVpnProtocol
{
public:
ShadowSocksVpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
virtual ~ShadowSocksVpnProtocol() override;
ErrorCode start() override;
void stop() override;

View file

@ -55,6 +55,7 @@ protected:
ConnectionState m_connectionState;
QString m_routeGateway;
QString m_vpnAddress;
QString m_vpnGateway;
QJsonObject m_rawConfig;

View file

@ -105,7 +105,7 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent
ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Protocol protocol)
{
qDebug() << "connectToVpn, CustomRouting is" << m_settings.customRouting();
//protocol = Protocol::ShadowSocks;
protocol = Protocol::ShadowSocks;
// TODO: Try protocols one by one in case of Protocol::Any
// TODO: Implement some behavior in case if connection not stable

BIN
deploy/data/macos/ss-local Executable file

Binary file not shown.

BIN
deploy/data/macos/ss-tunnel Executable file

Binary file not shown.

View file

@ -1,101 +0,0 @@
/*
* shadowsocks.h - Header files of library interfaces
*
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
*
* This file is part of the shadowsocks-libev.
* shadowsocks-libev is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* shadowsocks-libev is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with shadowsocks-libev; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef _SHADOWSOCKS_H
#define _SHADOWSOCKS_H
typedef struct {
/* Required */
char *remote_host; // hostname or ip of remote server
char *local_addr; // local ip to bind
char *method; // encryption method
char *password; // password of remote server
int remote_port; // port number of remote server
int local_port; // port number of local server
int timeout; // connection timeout
/* Optional, set NULL if not valid */
char *acl; // file path to acl
char *log; // file path to log
int fast_open; // enable tcp fast open
int mode; // enable udp relay
int mtu; // MTU of interface
int mptcp; // enable multipath TCP
int verbose; // verbose mode
} profile_t;
/* An example profile
*
* const profile_t EXAMPLE_PROFILE = {
* .remote_host = "example.com",
* .local_addr = "127.0.0.1",
* .method = "bf-cfb",
* .password = "barfoo!",
* .remote_port = 8338,
* .local_port = 1080,
* .timeout = 600;
* .acl = NULL,
* .log = NULL,
* .fast_open = 0,
* .mode = 0,
* .verbose = 0
* };
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*ss_local_callback)(int socks_fd, int udp_fd, void *data);
/*
* Create and start a shadowsocks local server.
*
* Calling this function will block the current thread forever if the server
* starts successfully.
*
* Make sure start the server in a separate process to avoid any potential
* memory and socket leak.
*
* If failed, -1 is returned. Errors will output to the log file.
*/
int start_ss_local_server(profile_t profile);
/*
* Create and start a shadowsocks local server, specifying a callback.
*
* The callback is invoked when the local server has started successfully. It passes the SOCKS
* server and UDP relay file descriptors, along with any supplied user data.
*
* Returns -1 on failure.
*/
int start_ss_local_server_with_callback(profile_t profile, ss_local_callback callback, void *udata);
#ifdef __cplusplus
}
#endif
// To stop the service on posix system, just kill the daemon process
// kill(pid, SIGKILL);
// Otherwise, If you start the service in a thread, you may need to send a signal SIGUSER1 to the thread.
// pthread_kill(pthread_t, SIGUSR1);
#endif // _SHADOWSOCKS_H

Binary file not shown.

View file

@ -55,27 +55,27 @@ int IpcServer::createPrivilegedProcess()
bool IpcServer::routeAdd(const QString &ip, const QString &gw, const QString &mask)
{
return Router::Instance().routeAdd(ip, gw, mask);
return Router::routeAdd(ip, gw, mask);
}
int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
{
return Router::Instance().routeAddList(gw, ips);
return Router::routeAddList(gw, ips);
}
bool IpcServer::clearSavedRoutes()
{
return Router::Instance().clearSavedRoutes();
return Router::clearSavedRoutes();
}
bool IpcServer::routeDelete(const QString &ip)
{
return Router::Instance().routeDelete(ip);
return Router::routeDelete(ip);
}
void IpcServer::flushDns()
{
return Router::Instance().flushDns();
return Router::flushDns();
}
bool IpcServer::checkAndInstallDriver()

View file

@ -1,327 +1,54 @@
#include "router.h"
#include <QProcess>
#ifdef Q_OS_WIN
#include "router_win.h"
#elif defined (Q_OS_MAC)
#include "router_mac.h"
#endif
Router &Router::Instance()
{
static Router s;
return s;
}
bool Router::routeAdd(const QString &ip, const QString &gw, QString mask)
{
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
.arg(ip)
.arg(mask)
.arg(gw);
#ifdef Q_OS_WIN
if (mask == "") {
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
}
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
MIB_IPFORWARDROW ipfrow;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAdd : GetBestInterface failed";
return false;
}
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
qDebug() << "Error: " << dwStatus;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
return (dwStatus == NO_ERROR);
#else
// Not implemented yet
return false;
return RouterWin::Instance().routeAdd(ip, gw, mask);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeAdd(ip, gw, mask);
#endif
}
int Router::routeAddList(const QString &gw, const QStringList &ips)
{
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
.arg(ips.size())
.arg(gw);
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
.arg(ips.join("\n"));
#ifdef Q_OS_WIN
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return 0;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return 0;
}
int success_count = 0;
QString mask;
MIB_IPFORWARDROW ipfrow;
ipfrow.dwForwardPolicy = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAddList : GetBestInterface failed";
return false;
}
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
for (int i = 0; i < ips.size(); ++i) {
QString ip = ips.at(i);
if (ip.isEmpty()) continue;
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
}
if (dwStatus == NO_ERROR) success_count++;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
return success_count;
#else
// Not implemented yet
return false;
return RouterWin::Instance().routeAddList(gw, ips);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeAddList(gw, ips);
#endif
}
bool Router::clearSavedRoutes()
{
#ifdef Q_OS_WIN
if (ipForwardRows.isEmpty()) return true;
qDebug() << "forward rows size:" << ipForwardRows.size();
// Declare and initialize variables
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
int removed_count = 0;
for (int i = 0; i < ipForwardRows.size(); ++i) {
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
}
else removed_count++;
}
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
ipForwardRows.clear();
return true;
#else
// Not implemented yet
return false;
return RouterWin::Instance().clearSavedRoutes();
#elif defined (Q_OS_MAC)
return RouterMac::Instance().clearSavedRoutes();
#endif
}
bool Router::routeDelete(const QString &ip)
{
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
#ifdef Q_OS_WIN
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("route delete %1")
.arg(ip);
p.start(command);
p.waitForFinished();
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
return true;
#else
// Not implemented yet
return false;
#endif
return RouterWin::Instance().routeDelete(ip);
#elif defined (Q_OS_MAC)
return RouterMac::Instance().routeDelete(ip);
#endif
}
void Router::flushDns()
{
#ifdef Q_OS_WIN
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("ipconfig /flushdns");
p.start(command);
p.waitForFinished();
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
RouterWin::Instance().flushDns();
#elif defined (Q_OS_MAC)
RouterMac::Instance().flushDns();
#endif
}

View file

@ -6,29 +6,7 @@
#include <QSettings>
#include <QHash>
#include <QDebug>
#ifdef Q_OS_WIN
#include <WinSock2.h> //includes Windows.h
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t u8_t ;
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif //Q_OS_WIN
#include <QObject>
/**
* @brief The Router class - General class for handling ip routing
@ -37,24 +15,12 @@ class Router : public QObject
{
Q_OBJECT
public:
static Router& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
Router() {}
Router(Router const &) = delete;
Router& operator= (Router const&) = delete;
#ifdef Q_OS_WIN
QList<MIB_IPFORWARDROW> ipForwardRows;
#endif
static bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
static int routeAddList(const QString &gw, const QStringList &ips);
static bool clearSavedRoutes();
static bool routeDelete(const QString &ip);
static void flushDns();
};
#endif // ROUTER_H

View file

@ -0,0 +1,69 @@
#include "router_mac.h"
#include <QProcess>
RouterMac &RouterMac::Instance()
{
static RouterMac s;
return s;
}
bool RouterMac::routeAdd(const QString &ip, const QString &gw, QString mask)
{
// route add -host ip gw
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("route", QStringList() << "add" << "-host" << ip << gw);
p.waitForFinished();
qDebug().noquote() << "OUTPUT routeAdd: " + p.readAll();
bool ok = (p.exitCode() == 0);
if (ok) {
m_addedRoutes.append(ip);
}
return ok;
}
int RouterMac::routeAddList(const QString &gw, const QStringList &ips)
{
int cnt = 0;
for (const QString &ip: ips) {
if (routeAdd(ip, gw)) cnt++;
}
return cnt;
}
bool RouterMac::clearSavedRoutes()
{
// No need to delete routes after iface down
return true;
// int cnt = 0;
// for (const QString &ip: m_addedRoutes) {
// if (routeDelete(ip)) cnt++;
// }
// return (cnt == m_addedRoutes.count());
}
bool RouterMac::routeDelete(const QString &ip)
{
// route delete ip gw
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("route", QStringList() << "delete" << ip);
p.waitForFinished();
qDebug().noquote() << "OUTPUT routeDelete: " + p.readAll();
return p.exitCode() == 0;}
void RouterMac::flushDns()
{
// sudo killall -HUP mDNSResponder
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
p.start("killall", QStringList() << "-HUP" << "mDNSResponder");
p.waitForFinished();
qDebug().noquote() << "OUTPUT killall -HUP mDNSResponder: " + p.readAll();
}

View file

@ -0,0 +1,38 @@
#ifndef ROUTERMAC_H
#define ROUTERMAC_H
#include <QTimer>
#include <QString>
#include <QSettings>
#include <QHash>
#include <QDebug>
#include <QObject>
/**
* @brief The Router class - General class for handling ip routing
*/
class RouterMac : public QObject
{
Q_OBJECT
public:
static RouterMac& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
RouterMac() {}
RouterMac(RouterMac const &) = delete;
RouterMac& operator= (RouterMac const&) = delete;
QList<QString> m_addedRoutes;
};
#endif // ROUTERMAC_H

View file

@ -0,0 +1,302 @@
#include "router_win.h"
#include <QProcess>
RouterWin &RouterWin::Instance()
{
static RouterWin s;
return s;
}
bool RouterWin::routeAdd(const QString &ip, const QString &gw, QString mask)
{
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
.arg(ip)
.arg(mask)
.arg(gw);
if (mask == "") {
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
}
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
MIB_IPFORWARDROW ipfrow;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAdd : GetBestInterface failed";
return false;
}
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
qDebug() << "Error: " << dwStatus;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
return (dwStatus == NO_ERROR);
}
int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
{
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
.arg(ips.size())
.arg(gw);
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
.arg(ips.join("\n"));
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Malloc failed. Out of memory.";
return 0;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "getIpForwardTable failed.";
if (pIpForwardTable)
free(pIpForwardTable);
return 0;
}
int success_count = 0;
QString mask;
MIB_IPFORWARDROW ipfrow;
ipfrow.dwForwardPolicy = 0;
ipfrow.dwForwardAge = 0;
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
// Set iface for route
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
qDebug() << "Router::routeAddList : GetBestInterface failed";
return false;
}
// Get TAP iface metric to set it for new routes
MIB_IPINTERFACE_ROW tap_iface;
InitializeIpInterfaceEntry(&tap_iface);
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
tap_iface.Family = AF_INET;
dwStatus = GetIpInterfaceEntry(&tap_iface);
if (dwStatus == NO_ERROR){
ipfrow.dwForwardMetric1 = tap_iface.Metric;
}
else {
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
ipfrow.dwForwardMetric1 = 256;
}
ipfrow.dwForwardMetric2 = 0;
ipfrow.dwForwardMetric3 = 0;
ipfrow.dwForwardMetric4 = 0;
ipfrow.dwForwardMetric5 = 0;
for (int i = 0; i < ips.size(); ++i) {
QString ip = ips.at(i);
if (ip.isEmpty()) continue;
mask = "255.255.255.255";
if (ip.endsWith(".0")) mask = "255.255.255.0";
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
// address
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
// mask
in_addr maskAddr;
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
dwStatus = CreateIpForwardEntry(&ipfrow);
if (dwStatus == NO_ERROR){
ipForwardRows.append(ipfrow);
//qDebug() << "Gateway changed successfully";
}
else {
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
}
if (dwStatus == NO_ERROR) success_count++;
}
// Free resources
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
return success_count;
}
bool RouterWin::clearSavedRoutes()
{
if (ipForwardRows.isEmpty()) return true;
qDebug() << "forward rows size:" << ipForwardRows.size();
// Declare and initialize variables
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
DWORD dwSize = 0;
BOOL bOrder = FALSE;
DWORD dwStatus = 0;
// Find out how big our buffer needs to be.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
// Allocate the memory for the table
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
return false;
}
// Now get the table.
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
if (pIpForwardTable)
free(pIpForwardTable);
return false;
}
int removed_count = 0;
for (int i = 0; i < ipForwardRows.size(); ++i) {
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
if (dwStatus != ERROR_SUCCESS) {
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
}
else removed_count++;
}
if (pIpForwardTable)
free(pIpForwardTable);
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
ipForwardRows.clear();
return true;
}
bool RouterWin:routeDelete(const QString &ip)
{
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("route delete %1")
.arg(ip);
p.start(command);
p.waitForFinished();
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
return true;
}
void RouterWin::flushDns()
{
QProcess p;
p.setProcessChannelMode(QProcess::MergedChannels);
QString command = QString("ipconfig /flushdns");
p.start(command);
p.waitForFinished();
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
}

View file

@ -0,0 +1,59 @@
#ifndef ROUTERWIN_H
#define ROUTERWIN_H
#include <QTimer>
#include <QString>
#include <QSettings>
#include <QHash>
#include <QDebug>
#include <QObject>
#ifdef Q_OS_WIN
#include <WinSock2.h> //includes Windows.h
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t u8_t ;
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif //Q_OS_WIN
/**
* @brief The Router class - General class for handling ip routing
*/
class RouterWin : public QObject
{
Q_OBJECT
public:
static RouterWin& Instance();
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
int routeAddList(const QString &gw, const QStringList &ips);
bool clearSavedRoutes();
bool routeDelete(const QString &ip);
void flushDns();
public slots:
private:
RouterWin() {}
RouterWin(RouterWin const &) = delete;
RouterWin& operator= (RouterWin const&) = delete;
#ifdef Q_OS_WIN
QList<MIB_IPFORWARDROW> ipForwardRows;
#endif
};
#endif // ROUTERWIN_H

View file

@ -25,10 +25,12 @@ SOURCES = \
win32 {
HEADERS += \
tapcontroller_win.h
tapcontroller_win.h \
router_win.h
SOURCES += \
tapcontroller_win.cpp
tapcontroller_win.cpp \
router_win.cpp
LIBS += \
-luser32 \
@ -40,6 +42,14 @@ LIBS += \
-lgdi32
}
macx {
HEADERS += \
router_mac.h
SOURCES += \
router_mac.cpp
}
include(../src/qtservice.pri)
#CONFIG(release, debug|release) {