diff --git a/core/DapGeoIP.h b/core/DapGeoIP.h index a6682327fd0e16b0e1a5c39d41357fda26ea94f9..333dbef22f30eb8de8d36b82d829a4c9f12cebbf 100644 --- a/core/DapGeoIP.h +++ b/core/DapGeoIP.h @@ -12,9 +12,8 @@ #include "maxminddb/maxminddb.h" -#define PATH_TO_DB ":/GeoLite2-Country.mmdb" -// #define PATH_TO_DB "/home/danilmartynenko/untitled/GeoLite2-Country.mmdb" -// #define PATH_TO_DB QCoreApplication::applicationDirPath() + "/GeoLite2-Country.mmdb" +#define PATH_TO_DB ":/GeoLite2-Country.mmdb" //21.02.2025 +// #define PATH_TO_DB ":/GeoLite2-City.mmdb" //21.02.2025 class DapGeoIP : public QObject { diff --git a/vpn/client/DapCmdHandlers/DapCmdConnect.cpp b/vpn/client/DapCmdHandlers/DapCmdConnect.cpp index fe9b4ffd0ac246df7614f06661d5d439679b2bbe..345c3c2620cb2466cf92a118fdb011df810ec3c0 100644 --- a/vpn/client/DapCmdHandlers/DapCmdConnect.cpp +++ b/vpn/client/DapCmdHandlers/DapCmdConnect.cpp @@ -71,7 +71,9 @@ void DapCmdConnect::handle(const QJsonObject* params) QMap<QString, QJsonValue> mandatoryConnParams = { {ADDRESS_KEY, params->value(ADDRESS_KEY)}, {PORT_KEY, params->value(PORT_KEY)}, - {UPDATE_ROUTE_TABLE, params->value(UPDATE_ROUTE_TABLE)} + {UPDATE_ROUTE_TABLE, params->value(UPDATE_ROUTE_TABLE)}, + {RUN_SCRIPT_PATH, params->value(RUN_SCRIPT_PATH)}, + {RUN_SCRIPT_AFTER_CONNECT, params->value(RUN_SCRIPT_AFTER_CONNECT)} }; if (!mandatoryConnParams[ADDRESS_KEY].isString() || !mandatoryConnParams[PORT_KEY].isDouble()) { @@ -93,6 +95,14 @@ void DapCmdConnect::handle(const QJsonObject* params) uint16_t port = uint16_t(mandatoryConnParams[PORT_KEY].toInt()); QString address = mandatoryConnParams[ADDRESS_KEY].toString(); + QString runScriptPath = mandatoryConnParams[RUN_SCRIPT_PATH].toString(); + + if (!runScriptPath.isEmpty()) { + bool runAfterConnect = mandatoryConnParams[RUN_SCRIPT_AFTER_CONNECT].toBool(true); + qDebug() << "Running script path:" << runScriptPath << "Run after connect:" << runAfterConnect; + emit sigSetRunScript(runScriptPath, runAfterConnect); + } + qDebug() << "Address:" << address << ", Port:" << port << ", UpdateRouteTable:" << updateRouteTable; qDebug() << "SerialKey:" << (serialKey.isEmpty() ? "No serial key provided" : "Serial key provided"); diff --git a/vpn/client/DapCmdHandlers/DapCmdConnect.h b/vpn/client/DapCmdHandlers/DapCmdConnect.h index 7786d0f50e589511a4e1baa569d9ff9ed5b68346..1d8bac67d60f5b43a5ce8c18606ed5e2b19c8553 100644 --- a/vpn/client/DapCmdHandlers/DapCmdConnect.h +++ b/vpn/client/DapCmdHandlers/DapCmdConnect.h @@ -24,6 +24,7 @@ signals: void sigConnectNoAuth(const QString& address, uint16_t port); void sigDisconnect(); void sigRestartService(bool if_runnning); + void sigSetRunScript(const QString& runScriptPath, bool runAfterConnect); private: const QString ACTION_KEY = "action"; @@ -33,6 +34,8 @@ private: const QString ADDRESS_KEY = "address"; const QString PORT_KEY = "port"; const QString UPDATE_ROUTE_TABLE = "updateRouteTable"; + const QString RUN_SCRIPT_PATH = "run_script_path"; + const QString RUN_SCRIPT_AFTER_CONNECT = "run_script_after_connect"; const QString CODE_KEY = "code"; const QString MESSAGE_KEY = "message"; diff --git a/vpn/client/darwin/DapUtun.cpp b/vpn/client/darwin/DapUtun.cpp index fbb024a11cb09319c23034ba1114bebd0c77571c..559473d2093525e51d54e1fe2607b1ff799b8a25 100644 --- a/vpn/client/darwin/DapUtun.cpp +++ b/vpn/client/darwin/DapUtun.cpp @@ -21,38 +21,11 @@ This file is part of DAP UI SDK the open source project along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. */ -#include <QtDebug> -#include <QProcess> -#include <QFile> - -#include <stdarg.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/sys_domain.h> -#include <sys/kern_control.h> -#include <net/if_utun.h> -#include <errno.h> -#include <string.h> - -#include <unistd.h> -#include <fcntl.h> - - - #include "DapUtils.h" -#include "DapTunUnixAbstract.h" -#include "QtCore/qdebug.h" #include "darwin/DapUtun.h" -#include "DapTunWorkerUnix.h" -#include "DapNetworkMonitor.h" -#include "SigUnixHandler.h" -/** - * @brief DapUtun::DapUtun - */ DapUtun::DapUtun() - :DapTunUnixAbstract() + : DapTunUnixAbstract() { // List of Apple addresses needed to be routed directly through the tunnel appleAdditionalRoutes = QStringList({ @@ -71,50 +44,81 @@ DapUtun::DapUtun() this, &DapUtun::tunDeviceDestroy, Qt::DirectConnection); } -/** - * @brief DapUtun::requestTunDeviceCreate - */ + +void DapUtun::executeCommand(const QString &cmd) +{ + qDebug() << "Executing command:" << cmd; + + QProcess process; + process.start(cmd); + + if (!process.waitForFinished(2000)) { + qWarning() << "Command failed to execute:" << process.errorString(); + return; + } + + int ret = process.exitCode(); + QString output = process.readAllStandardOutput(); + QString errorOutput = process.readAllStandardError(); + + qDebug() << "Command output:" << output.trimmed(); + if (!errorOutput.isEmpty()) { + qWarning() << "Command error output:" << errorOutput.trimmed(); + } + + qDebug() << "Command returned exit code:" << ret; + if (ret != 0) { + qWarning() << "Command failed with exit code:" << ret; + } +} + void DapUtun::tunDeviceCreate() { qDebug() << "[DapUtun::tunDeviceCreate]"; + if (m_tunSocket > 0) { qInfo() << "Socket already open"; return; } - // Prepare structs + + // Lambda function to retrieve error messages + auto getErrorString = []() -> QString { + int err = errno; + char errbuf[256]; + ::strerror_r(err, errbuf, sizeof(errbuf)); + return QString("%1 (code %2)").arg(errbuf).arg(err); + }; + + // Initialize the control structure struct ctl_info l_ctl_info = {0}; // Copy utun control name if (::strlcpy(l_ctl_info.ctl_name, UTUN_CONTROL_NAME, sizeof(l_ctl_info.ctl_name)) - >= sizeof(l_ctl_info.ctl_name)){ - emit error( QString("UTUN_CONTROL_NAME % is too long").arg(UTUN_CONTROL_NAME)); + >= sizeof(l_ctl_info.ctl_name)) { + emit error(QString("UTUN_CONTROL_NAME %1 is too long").arg(UTUN_CONTROL_NAME)); return; } // Create utun socket int l_tun_fd = ::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); - if( l_tun_fd < 0){ - int l_errno = errno; - char l_errbuf[256]; - ::strerror_r(l_errno, l_errbuf,sizeof(l_errbuf)); - emit error ( QString("Opening utun device control (SYSPROTO_CONTROL) error: '%1' (code %2)").arg(l_errbuf).arg(l_errno)); + if (l_tun_fd < 0) { + emit error(QString("Opening utun device control (SYSPROTO_CONTROL) error: %1") + .arg(getErrorString())); return; } qInfo() << "Utun SYSPROTO_CONTROL descriptor obtained"; - // Pass control structure to the utun socket - if( ::ioctl(l_tun_fd, CTLIOCGINFO, &l_ctl_info ) < 0 ){ - int l_errno = errno; - char l_errbuf[256]; - ::strerror_r(l_errno, l_errbuf,sizeof(l_errbuf)); - emit error (QString("Can't execute ioctl(CTLIOCGINFO): '%1' (code %1)").arg(l_errbuf).arg(l_errno)); + // Retrieve control ID via ioctl + if (::ioctl(l_tun_fd, CTLIOCGINFO, &l_ctl_info) < 0) { + ::close(l_tun_fd); + emit error(QString("Can't execute ioctl(CTLIOCGINFO): %1").arg(getErrorString())); return; } qInfo() << "Utun CTLIOCGINFO structure passed through ioctl"; // Trying to connect with one of utunX devices int l_ret = -1; - for(int l_unit = 0; l_unit < 256; l_unit++){ + for (int l_unit = 0; l_unit < 256; l_unit++) { struct sockaddr_ctl l_sa_ctl = {0}; l_sa_ctl.sc_id = l_ctl_info.ctl_id; l_sa_ctl.sc_len = sizeof(l_sa_ctl); @@ -122,234 +126,323 @@ void DapUtun::tunDeviceCreate() l_sa_ctl.ss_sysaddr = AF_SYS_CONTROL; l_sa_ctl.sc_unit = l_unit + 1; - // If connect successful, new utunX device should be created - l_ret = ::connect(l_tun_fd, (struct sockaddr *)&l_sa_ctl, sizeof(l_sa_ctl)); - if(l_ret == 0) + l_ret = ::connect(l_tun_fd, reinterpret_cast<struct sockaddr*>(&l_sa_ctl), sizeof(l_sa_ctl)); + if (l_ret == 0) break; } - // Check if the previous step was successful - if (l_ret < 0){ - int l_errno = errno; - char l_errbuf[256]; - ::strerror_r(l_errno, l_errbuf,sizeof(l_errbuf)); - emit error( QString("Can't create utun device: '%1' (code %2)").arg(l_errbuf).arg(l_errno)); + + if (l_ret < 0) { + ::close(l_tun_fd); + emit error(QString("Can't create utun device: %1").arg(getErrorString())); return; } - // Get iface name of newly created utun dev. qInfo() << "Utun device created"; - char l_utunname[20]; - l_utunname[0] = '\0'; + + // Retrieve the name of the utun network interface + char l_utunname[20] = {0}; socklen_t l_utunname_len = sizeof(l_utunname); - if (::getsockopt(l_tun_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, l_utunname, &l_utunname_len) ){ - int l_errno = errno; - char l_errbuf[256]; - ::strerror_r(l_errno, l_errbuf,sizeof(l_errbuf)); - emit error ( QString("Can't get utun device name: '%s' (code %d)").arg(l_errbuf).arg(l_errno)); + if (::getsockopt(l_tun_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, l_utunname, &l_utunname_len)) { + ::close(l_tun_fd); + emit error(QString("Can't get utun device name: %1").arg(getErrorString())); return; } - m_tunDeviceName = QString::fromLatin1(l_utunname,l_utunname_len-1); - qInfo() << "Created utun "<<m_tunDeviceName<<" network interface"; /* this is the special file descriptor that the caller will use to talk * with the virtual interface */ + m_tunDeviceName = QString::fromLatin1(l_utunname, l_utunname_len - 1); + qInfo() << "Created utun" << m_tunDeviceName << "network interface"; + m_tunSocket = l_tun_fd; } -/** - * @brief Detects active interface by default routing to 8.8.8.8 - * @return Returns current active internet interface - */ QString DapUtun::getInternetInterface() { return DapUtils::shellCmd("route get 8.8.8.8 | grep interface | awk '{print $2;}'"); } +void DapUtun::saveCurrentConnectionInterfaceData() { + m_lastUsedConnectionDevice = getInternetInterface(); + qDebug() << "Current internet interface:" << m_lastUsedConnectionDevice; -/** - * @brief DapUtun::saveCurrentConnectionInterfaceData - */ -void DapUtun::saveCurrentConnectionInterfaceData() -{ - - m_lastUsedConnectionDevice = this->getInternetInterface(); - qDebug() << "Current internet interface "<< m_lastUsedConnectionDevice; - QString result = DapUtils::shellCmd(QString("networksetup -listnetworkserviceorder | grep 'Hardware Port' | grep %1").arg(m_lastUsedConnectionDevice)); - - QStringList res1 = result.split(":"); // Break down answer by Hardware port: and Device + QString cmdList = QString("networksetup -listnetworkserviceorder | grep 'Hardware Port' | grep %1") + .arg(m_lastUsedConnectionDevice); + QString result = DapUtils::shellCmd(cmdList); - if(res1.length() < 3) { - qWarning() << "Can't get current connection interface name! Command returns "<< result; + QStringList parts = result.split(":"); + if (parts.length() < 3) { + qWarning() << "Unable to retrieve connection interface name! Command output:" << result; return; } - QStringList res2 = res1[1].split(","); // Split from ,Device - - if(res2.length() < 2) { - qWarning() << " Wrong networksetup answer! Command returns "<< result; + QStringList interfaceParts = parts[1].split(","); + if (interfaceParts.length() < 2) { + qWarning() << "Unexpected format from networksetup command! Command output:" << result; return; } - m_lastUsedConnectionName = res2[0].trimmed(); - qDebug() << "Current internet connection name" << m_lastUsedConnectionName; + m_lastUsedConnectionName = interfaceParts[0].trimmed(); + qDebug() << "Current internet connection name:" << m_lastUsedConnectionName; - result = DapUtils::shellCmd(QString("networksetup -getinfo \"%1\" | grep Router").arg(m_lastUsedConnectionName)); - QStringList res3 =result.split("\n", Qt::SkipEmptyParts); - if(res3.length() < 1) { - qWarning() << "No default router at all"; - }else{ - QStringList res4 = res3[0].split(":"); - if(res3.length() != 2) { - qWarning() << "No default route address in line"; - }else{ - m_defaultGwOld = res4[1] == "none" ? QString() : res4[1]; + QString cmdGetInfo = QString("networksetup -getinfo \"%1\" | grep Router") + .arg(m_lastUsedConnectionName); + result = DapUtils::shellCmd(cmdGetInfo); + + QStringList infoLines = result.split("\n", Qt::SkipEmptyParts); + if (infoLines.isEmpty()) { + qWarning() << "No default router found."; + } else { + QStringList routerParts = infoLines.first().split(":"); + if (routerParts.length() != 2) { + qWarning() << "Unexpected default router line format:" << infoLines.first(); + } else { + QString routerValue = routerParts[1].trimmed(); + m_defaultGwOld = (routerValue == "none") ? QString() : routerValue; } } - qInfo() << "DeviceName name:" << m_lastUsedConnectionDevice - << "Interface Name:" << m_lastUsedConnectionName - << "Router" << m_defaultGwOld - ; + qInfo() << "Device Name:" << m_lastUsedConnectionDevice + << "Interface Name:" << m_lastUsedConnectionName + << "Router:" << m_defaultGwOld; +} + +QStringList DapUtun::getNetworkServices() { + QProcess process; + process.start("networksetup", QStringList() << "-listallnetworkservices"); + + if (!process.waitForFinished(5000)) { + qWarning() << "Failed to get list of network services:" << process.errorString(); + return QStringList(); + } + + QStringList lines = QString(process.readAllStandardOutput()).split("\n", Qt::SkipEmptyParts); + if (lines.size() < 2) { + qWarning() << "No valid network services found."; + return QStringList(); + } + + return lines.mid(1); } +QString DapUtun::getDNSServers(const QString &service) { + QProcess process; + process.start("networksetup", QStringList() << "-getdnsservers" << service); + if (!process.waitForFinished(5000)) { + qWarning() << "Timeout while getting DNS for service" << service << ":" << process.errorString(); + return QString(); + } + + return process.readAllStandardOutput().trimmed(); +} -/** - * @brief DapUtun::onWorkerStarted - */ -void DapUtun::onWorkerStarted() -{ - qDebug() << "tunnelCreate()"; +bool DapUtun::setDNS(const QString &service, const QString &dnsServer) { QProcess process; + QStringList arguments; - if(m_tunSocket <= 0) { - qCritical() << "Can't bring up network interface"; - return; + if (dnsServer.isEmpty()) { + arguments << "-setdnsservers" << service << "empty"; + } else { + arguments << "-setdnsservers" << service << dnsServer; } - saveCurrentConnectionInterfaceData(); + process.start("networksetup", arguments); + if (!process.waitForFinished(5000)) { + qWarning() << "Timeout while configuring DNS for service" << service << ":" << process.errorString(); + return false; + } -// networksetup -ordernetworkservices + if (process.exitCode() != 0) { + qWarning() << "Command failed for service" << service << "with exit code:" << process.exitCode(); + qWarning() << "Error output: " << process.readAllStandardError(); + return false; + } - // Update route table for upstream if it's not local - if(!isLocalAddress(upstreamAddress())) { - QString run = QString("route add -host %2 %1") - .arg(m_defaultGwOld).arg(upstreamAddress()); - qDebug() << "Execute " << run; - ::system(run.toLatin1().constData()); + return true; +} + +//Not used because breaks other interfaces. At the moment, only the utun interface is being modified. +void DapUtun::configureDNS(const QMap<QString, QString> &dnsMap) { + QStringList services = getNetworkServices(); + if (services.isEmpty()) { + qWarning() << "No network services detected."; + return; } - // Add all CDBs into routing exeption - for (const auto &str : m_routingExceptionAddrs){ - QString run = QString("route add -host %2 %1") - .arg(m_defaultGwOld).arg(str); - qDebug() << "Execute " << run; - ::system(run.toLatin1().constData() ); + for (const QString &service : services) { + QString dnsServer = dnsMap.value(service, ""); + if (setDNS(service, dnsServer)) { + qDebug() << "DNS servers successfully " << (dnsServer.isEmpty() ? "cleared" : "configured") << " for service:" << service; + } else { + qWarning() << "Failed to " << (dnsServer.isEmpty() ? "clear" : "configure") << " DNS servers for service:" << service; + } } +} +void DapUtun::clearAllDNS() { + configureDNS(QMap<QString, QString>()); +} - // Create connection - /* - QString cmdConnAdd = QString( - "networksetup -createnetworkservice %1 %2 ;" - "networksetup -setnetworkserviceenabled %1 on;" - "networksetup -setmanual %1 %3 255.255.255.255 %4;" - "networksetup -setdnsservers %4" - ) .arg (DAP_BRAND) - .arg(tunDeviceName()) - .arg(addr()) - .arg(gw()); +void DapUtun::onWorkerStarted() +{ + qDebug() << "Starting tunnel creation"; - qDebug() << "[Cmd to created interface: " << cmdConnAdd; + if (m_tunSocket <= 0) { + qCritical() << "Failed to bring up network interface: invalid tunnel socket"; + return; + } - DapTunUnixAbstract::runShellCmd( cmdConnAdd ); + saveCurrentConnectionInterfaceData(); + m_currentInterface = getCurrentNetworkInterface(); + if (m_currentInterface.isEmpty()) { + qCritical() << "No active network interface found!"; + return; + } - // Add additional Apple routes - QString cmdAddAdditionalRoutes = QString("networksetup -setadditionalroutes %1").arg(DAP_BRAND); - foreach(QString additionalRoute, appleAdditionalRoutes){ - cmdAddAdditionalRoutes += additionalRoute; - cmdAddAdditionalRoutes += "\"\""; + if (!isLocalAddress(upstreamAddress())) { + QString run = QString("route add -host %2 %1") + .arg(m_defaultGwOld) + .arg(upstreamAddress()); + executeCommand(run); } - DapTunUnixAbstract::runShellCmd( cmdAddAdditionalRoutes);*/ + for (const auto &address : m_routingExceptionAddrs) { + QString run = QString("route add -host %2 %1") + .arg(m_defaultGwOld) + .arg(address); + executeCommand(run); + } + // QString cmdConnAdd = QString( + // "networksetup -createnetworkservice %1 %2 ;" + // "networksetup -setnetworkserviceenabled %1 on;" + // "networksetup -setmanual %1 %3 255.255.255.255 %4;" + // ).arg(DAP_BRAND) + // .arg(tunDeviceName()) + // .arg(addr()) + // .arg(gw()); - // Bring up network interface - ::system(QString("ifconfig %1 %2 %3") - .arg(tunDeviceName()).arg(addr()).arg(gw()).toLatin1().constData()); + // qDebug() << "Network service creation command:" << cmdConnAdd; + // DapUtils::shellCmd(cmdConnAdd); - qDebug() << "Configured " << tunDeviceName() << " with " << addr() << "-->" << gw(); + QString cmdSetDNS = QString("networksetup -setdnsservers %1 %2") + .arg(m_currentInterface) + .arg(gw()); - // Remove old default route - ::system(QString("route delete default").toLatin1().constData()); + qDebug() << "Setting DNS for" << m_currentInterface << "to:" << gw(); + DapUtils::shellCmd(cmdSetDNS); - qDebug() << "Removed default route " << m_defaultGwOld; + QString ifconfigCmd = QString("ifconfig %1 %2 %3") + .arg(tunDeviceName()) + .arg(addr()) + .arg(gw()); + executeCommand(ifconfigCmd); + qDebug() << "Configured interface" << tunDeviceName() << "with IP" << addr() << "and gateway" << gw(); + + executeCommand("route delete default"); + qDebug() << "Removed default route:" << m_defaultGwOld; - // Setup default route QString gateway = gw(); qDebug() << "Gateway obtained: " << gateway; if (!gateway.isEmpty()) { - ::system(QString("route add default %1").arg(gateway).toLatin1().constData()); - qDebug() << "Added default route " << gateway; + executeCommand(QString("route add default %1").arg(gateway)); + qDebug() << "Added default route:" << gateway; - ::system(QString("route add -net 224.0.0.0/4 %1").arg(gateway).toLatin1().constData()); - qDebug() << "Added multicast route " << gateway; + executeCommand(QString("route add -net 224.0.0.0/4 %1").arg(gateway)); + qDebug() << "Added multicast route via gateway:" << gateway; - ::system(QString("route add -net 255.255.255.255/32 %1").arg(gateway).toLatin1().constData()); - qDebug() << "Added broadcast route " << gateway; + executeCommand(QString("route add -net 255.255.255.255/32 %1").arg(gateway)); + qDebug() << "Added broadcast route via gateway:" << gateway; } else { - qWarning() << "Gateway is empty!"; + qWarning() << "Gateway is empty, cannot set up default routes"; } - // Add additional Apple routes - foreach(QString additionalRoute, appleAdditionalRoutes) { - QStringList routeArgs = additionalRoute.split(QRegExp("\\s+"), Qt::SkipEmptyParts); - - if(routeArgs.length() == 1) { - ::system(QString("route add -host %1 %2") - .arg(routeArgs[0]).arg(gw()).toLatin1().constData()); - qDebug() << "Added host route " << routeArgs[0] << " via " << gw(); - } else if (routeArgs.length() == 2) { - ::system(QString("route add -net %1 %2 %3") - .arg(routeArgs[0]).arg(routeArgs[1]).arg(gw()).toLatin1().constData()); - qDebug() << "Added network route " << routeArgs[0] << "/" << routeArgs[1] << " via " << gw(); - } else { - qWarning() << "Unparseable additional route line " << additionalRoute; - } + QString cmdAddAdditionalRoutes = QString("networksetup -setadditionalroutes %1").arg(DAP_BRAND); + foreach (const QString &additionalRoute, appleAdditionalRoutes) { + cmdAddAdditionalRoutes += " " + additionalRoute; } + qDebug() << "Additional Apple routes command:" << cmdAddAdditionalRoutes; + DapUtils::shellCmd(cmdAddAdditionalRoutes); m_isCreated = true; emit created(); } +QString DapUtun::getCurrentNetworkInterface() +{ + qDebug() << "Fetching current active network interface..."; + + QProcess routeProcess; + routeProcess.start("route get default"); + routeProcess.waitForFinished(); + QString routeOutput = routeProcess.readAllStandardOutput(); + + qDebug() << "Raw route get default output:" << routeOutput; + + QString activeInterface; + QStringList routeLines = routeOutput.split("\n"); + for (const QString &line : routeLines) { + if (line.contains("interface:")) { + activeInterface = line.split(":").last().trimmed(); + qDebug() << "Detected active interface:" << activeInterface; + break; + } + } + + if (activeInterface.isEmpty()) { + qWarning() << "No active internet interface found!"; + return QString(); + } + + QProcess serviceOrderProcess; + serviceOrderProcess.start("networksetup -listnetworkserviceorder"); + serviceOrderProcess.waitForFinished(); + QString serviceOrderOutput = serviceOrderProcess.readAllStandardOutput(); + + qDebug() << "Raw network service order output:" << serviceOrderOutput; + + QString regexPattern = R"(\(Hardware Port: (.+?), Device: )" + QRegularExpression::escape(activeInterface) + R"(\))"; + QRegularExpression regex(regexPattern); + QRegularExpressionMatch match = regex.match(serviceOrderOutput); + + if (match.hasMatch()) { + QString matchedService = match.captured(1).trimmed(); + qDebug() << "Matched active network service via service order:" << matchedService; + return matchedService; + } + + qWarning() << "Could not match active interface to a network service!"; + return QString(); +} -/** - * @brief DapUtun::onWorkerStopped - */ void DapUtun::tunDeviceDestroy() { DapTunUnixAbstract::tunDeviceDestroy(); - // Delete upstream routes - if(!isLocalAddress(upstreamAddress())) - { - QString run = QString("route delete -host %1") - .arg(upstreamAddress()) ; - qDebug() << "Execute "<<run; - ::system( run.toLatin1().constData() ); + + if (!isLocalAddress(upstreamAddress())){ + QString cmdDeleteUpstream = QString("route delete -host %1") + .arg(upstreamAddress()); + executeCommand(cmdDeleteUpstream); } - for (const auto &str : m_routingExceptionAddrs){ - QString run = QString("route delete -host %1") - .arg(str); - qDebug() << "Execute " << run; - ::system(run.toLatin1().constData() ); + for (const auto &route : m_routingExceptionAddrs) { + QString cmdDeleteException = QString("route delete -host %1") + .arg(route); + executeCommand(cmdDeleteException); } // Other routes connected with tunnel should be destroyed autimaticly // Restore default gateway - ::system ( QString("route add default %1").arg(m_defaultGwOld).toLatin1().constData()); - qInfo() << "Restored default route "<< m_defaultGwOld; + QString cmdRestoreDefault = QString("route add default %1") + .arg(m_defaultGwOld); + executeCommand(cmdRestoreDefault); + qInfo() << "Restored default route:" << m_defaultGwOld; + + QString cmdRestoreDNS = QString("networksetup -setdnsservers \"%1\" %2") + .arg(m_currentInterface) + .arg(m_defaultGwOld); + executeCommand(cmdRestoreDNS); + qInfo() << "Restored DNS settings for" << m_currentInterface << "to" << m_defaultGwOld; } - diff --git a/vpn/client/darwin/DapUtun.h b/vpn/client/darwin/DapUtun.h index 22cde280f2cd6557261ea096560b6c803af4deb6..1659a4fc29422515060a3f16d2d9875b4af0cfe9 100644 --- a/vpn/client/darwin/DapUtun.h +++ b/vpn/client/darwin/DapUtun.h @@ -20,7 +20,28 @@ This file is part of DAP UI SDK the open source project You should have received a copy of the GNU General Public License along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. */ + #pragma once + +#include <QtDebug> +#include <QProcess> +#include <QFile> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sys_domain.h> +#include <sys/kern_control.h> +#include <net/if_utun.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "DapTunUnixAbstract.h" +#include "DapTunWorkerUnix.h" +#include "DapNetworkMonitor.h" +#include "SigUnixHandler.h" #include "DapTunUnixAbstract.h" class DapUtun : public DapTunUnixAbstract @@ -29,9 +50,14 @@ public: DapUtun(); ~DapUtun() {} protected: + void executeCommand(const QString &cmd); void tunDeviceCreate(); void tunDeviceDestroy(); void onWorkerStarted(); + void clearAllDNS(); + void configureDNS(const QMap<QString, QString> &dnsMap); + QString getCurrentNetworkInterface(); + // Getting currently using connection interface name from nmcli command-line tool // and save to m_lastUsedConnectionName and m_lastUsedConnectionDevice @@ -39,14 +65,23 @@ protected: private: + QStringList getNetworkServices(); + QString getDNSServers(const QString &service); + bool setDNS(const QString &service, const QString &dnsServer); + // Connection witch used before creating utun Interface QString m_lastUsedConnectionName; QString m_lastUsedConnectionDevice; + QString m_currentInterface; + QStringList appleAdditionalRoutes; // List of Apple addresses needed to be routed directly through the tunnel - QString runShellCmd(const QString& cmd); + // QString runShellCmd(const QString& cmd); // Get current active internet interface QString getInternetInterface(); + + QMap<QString, QString> m_dnsMap; + }; diff --git a/vpn/qml/handlers/DapCmdConnect.cpp b/vpn/qml/handlers/DapCmdConnect.cpp index 3da24dc601341b1e1837ad77be67e2770df27406..56aabea35e365fb5b87a3d7bce32824ed16cfb27 100644 --- a/vpn/qml/handlers/DapCmdConnect.cpp +++ b/vpn/qml/handlers/DapCmdConnect.cpp @@ -7,7 +7,8 @@ DapCmdConnect::DapCmdConnect(QObject *parent) } void DapCmdConnect::sendCmdConnect(const QString& a_addr, quint16 a_port, - const QString& a_user, const QString& a_pswd, const QString& a_serial, const bool a_updateRouteTable) + const QString& a_user, const QString& a_pswd, const QString& a_serial, const bool a_updateRouteTable, + const QString& a_runScriptPath, const bool a_runAfterConnect) { QJsonObject obj; obj["action"] = "Connect"; @@ -20,7 +21,10 @@ void DapCmdConnect::sendCmdConnect(const QString& a_addr, quint16 a_port, obj["password"] = a_pswd; if ( !a_serial.isEmpty() ) obj["serial"] = QString(a_serial).remove('-'); - obj["port"] = a_port; + if ( !a_runScriptPath.isEmpty() ) { + obj["run_script_path"] = QString(a_runScriptPath); + obj["run_script_after_connect"] = a_runAfterConnect; + } sendCmd(&obj); } diff --git a/vpn/qml/handlers/DapCmdConnect.h b/vpn/qml/handlers/DapCmdConnect.h index 552bd94a6c1a0117df1e0dc04022c34379d2f764..aa873fea639f887296ac2fa893377703e070577d 100644 --- a/vpn/qml/handlers/DapCmdConnect.h +++ b/vpn/qml/handlers/DapCmdConnect.h @@ -11,8 +11,9 @@ public: explicit DapCmdConnect(QObject *parent = nullptr); public slots: - void sendCmdConnect(const QString& a_addr, quint16 a_port, - const QString& a_user= QString(), const QString& a_pswd = QString(), const QString& a_serial= QString(), const bool a_updateRouteTable = true); + void sendCmdConnect(const QString& a_addr, quint16 a_port, const QString& a_user = QString(), const QString& a_pswd = QString(), + const QString& a_serial = QString(), const bool a_updateRouteTable = true, + const QString& a_runScriptPath = QString(), const bool a_runAfterConnect = false); void sendCmdConnectByOrder(const QString& a_addr);