diff --git a/client/core/defs.h b/client/core/defs.h index e433f142..27077cc7 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -56,6 +56,7 @@ enum ErrorCode // Distro errors OpenVpnExecutableMissing, EasyRsaExecutableMissing, + ShadowSocksExecutableMissing, AmneziaServiceConnectionFailed, // VPN errors diff --git a/client/core/openvpnconfigurator.cpp b/client/core/openvpnconfigurator.cpp index 5b644e0b..9e113925 100644 --- a/client/core/openvpnconfigurator.cpp +++ b/client/core/openvpnconfigurator.cpp @@ -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; } diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index a1591d55..8162c112 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -104,12 +104,13 @@ void OpenVpnProtocol::sendManagementCommand(const QString& command) QIODevice *device = dynamic_cast(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 = ""; } diff --git a/client/protocols/openvpnprotocol.h b/client/protocols/openvpnprotocol.h index af3ed47a..db4b0802 100644 --- a/client/protocols/openvpnprotocol.h +++ b/client/protocols/openvpnprotocol.h @@ -47,7 +47,7 @@ private: private: void updateRouteGateway(QString line); - void updateVpnGateway(); + void updateVpnGateway(const QString &line); QSharedPointer m_openVpnProcess; }; diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index ce4dbcd7..f853cf69 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -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() diff --git a/client/protocols/shadowsocksvpnprotocol.h b/client/protocols/shadowsocksvpnprotocol.h index 8c2b58ef..7462aa4c 100644 --- a/client/protocols/shadowsocksvpnprotocol.h +++ b/client/protocols/shadowsocksvpnprotocol.h @@ -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; diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h index 71d24a35..133fd64f 100644 --- a/client/protocols/vpnprotocol.h +++ b/client/protocols/vpnprotocol.h @@ -55,6 +55,7 @@ protected: ConnectionState m_connectionState; QString m_routeGateway; + QString m_vpnAddress; QString m_vpnGateway; QJsonObject m_rawConfig; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 55fa5fb0..ba1302b0 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -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 diff --git a/deploy/data/macos/ss-local b/deploy/data/macos/ss-local new file mode 100755 index 00000000..b1e501a3 Binary files /dev/null and b/deploy/data/macos/ss-local differ diff --git a/deploy/data/macos/ss-tunnel b/deploy/data/macos/ss-tunnel new file mode 100755 index 00000000..e5fc361c Binary files /dev/null and b/deploy/data/macos/ss-tunnel differ diff --git a/deploy/data/windows/ss/shadowsocks.h b/deploy/data/windows/ss/shadowsocks.h deleted file mode 100644 index 6c36611f..00000000 --- a/deploy/data/windows/ss/shadowsocks.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * shadowsocks.h - Header files of library interfaces - * - * Copyright (C) 2013 - 2019, Max Lv - * - * 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 - * . - */ - -#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 diff --git a/deploy/data/windows/ss/ss-server.exe b/deploy/data/windows/ss/ss-server.exe deleted file mode 100644 index 5df39a0c..00000000 Binary files a/deploy/data/windows/ss/ss-server.exe and /dev/null differ diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index fbe83fde..d366d6b7 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -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() diff --git a/service/server/router.cpp b/service/server/router.cpp index f642be23..a046db87 100644 --- a/service/server/router.cpp +++ b/service/server/router.cpp @@ -1,327 +1,54 @@ #include "router.h" -#include +#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 } + diff --git a/service/server/router.h b/service/server/router.h index 655bf90b..dcc32faa 100644 --- a/service/server/router.h +++ b/service/server/router.h @@ -6,29 +6,7 @@ #include #include #include - - -#ifdef Q_OS_WIN -#include //includes Windows.h -#include - - -#include -#include -#include -#include - - -#include -typedef uint8_t u8_t ; - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - - -#endif //Q_OS_WIN - +#include /** * @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 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 + diff --git a/service/server/router_mac.cpp b/service/server/router_mac.cpp new file mode 100644 index 00000000..d6c67455 --- /dev/null +++ b/service/server/router_mac.cpp @@ -0,0 +1,69 @@ +#include "router_mac.h" + +#include + +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(); +} diff --git a/service/server/router_mac.h b/service/server/router_mac.h new file mode 100644 index 00000000..07b7112c --- /dev/null +++ b/service/server/router_mac.h @@ -0,0 +1,38 @@ +#ifndef ROUTERMAC_H +#define ROUTERMAC_H + +#include +#include +#include +#include +#include +#include + + +/** + * @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 m_addedRoutes; +}; + +#endif // ROUTERMAC_H + diff --git a/service/server/router_win.cpp b/service/server/router_win.cpp new file mode 100644 index 00000000..5fedd1e5 --- /dev/null +++ b/service/server/router_win.cpp @@ -0,0 +1,302 @@ +#include "router_win.h" + +#include + +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(); +} diff --git a/service/server/router_win.h b/service/server/router_win.h new file mode 100644 index 00000000..91765d4b --- /dev/null +++ b/service/server/router_win.h @@ -0,0 +1,59 @@ +#ifndef ROUTERWIN_H +#define ROUTERWIN_H + +#include +#include +#include +#include +#include +#include + + +#ifdef Q_OS_WIN +#include //includes Windows.h +#include + + +#include +#include +#include +#include + + +#include +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 ipForwardRows; +#endif +}; + +#endif // ROUTERWIN_H diff --git a/service/server/server.pro b/service/server/server.pro index 64c2e895..5b4846f6 100644 --- a/service/server/server.pro +++ b/service/server/server.pro @@ -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) {