#include "xrayprotocol.h" #include #include #include #include #include "core/networkUtilities.h" #include "utilities.h" XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent) { readXrayConfiguration(configuration); m_routeGateway = NetworkUtilities::getGatewayAndIface(); m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr; m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr; m_t2sProcess = IpcClient::InterfaceTun2Socks(); } XrayProtocol::~XrayProtocol() { qDebug() << "XrayProtocol::~XrayProtocol()"; XrayProtocol::stop(); } ErrorCode XrayProtocol::start() { qDebug().noquote() << "XrayProtocol xrayExecPath():" << xrayExecPath(); if (!QFileInfo::exists(xrayExecPath())) { setLastError(ErrorCode::XrayExecutableMissing); return lastError(); } #ifdef QT_DEBUG m_xrayCfgFile.setAutoRemove(false); #endif m_xrayCfgFile.open(); QString config = QJsonDocument(m_xrayConfig).toJson(); config.replace(m_remoteHost, m_remoteAddress); m_xrayCfgFile.write(config.toUtf8()); m_xrayCfgFile.close(); QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json"; qDebug().noquote() << "XrayProtocol::start()" << xrayExecPath() << args.join(" "); m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels); m_xrayProcess.setProgram(xrayExecPath()); if (Utils::processIsRunning(Utils::executable("xray", false))) { qDebug().noquote() << "kill previos xray"; Utils::killProcessByName(Utils::executable("xray", false)); } m_xrayProcess.setArguments(args); connect(&m_xrayProcess, &QProcess::readyReadStandardOutput, this, [this]() { #ifdef QT_DEBUG qDebug().noquote() << "xray:" << m_xrayProcess.readAllStandardOutput(); #endif }); connect(&m_xrayProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus; setConnectionState(Vpn::ConnectionState::Disconnected); if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed); emit setConnectionState(Vpn::ConnectionState::Error); } }); m_xrayProcess.start(); m_xrayProcess.waitForStarted(); if (m_xrayProcess.state() == QProcess::ProcessState::Running) { setConnectionState(Vpn::ConnectionState::Connecting); QThread::msleep(1000); return startTun2Sock(); } else return ErrorCode::XrayExecutableMissing; } ErrorCode XrayProtocol::startTun2Sock() { m_t2sProcess->start(); #ifdef Q_OS_WIN m_configData.insert("inetAdapterIndex", NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress))); #endif connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this, [&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; }); connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this, [&](int vpnState) { qDebug() << "PrivilegedProcess setConnectionState " << vpnState; if (vpnState == Vpn::ConnectionState::Connected) { setConnectionState(Vpn::ConnectionState::Connecting); QList dnsAddr; dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString())); // We don't use secondary DNS if primary DNS is AmneziaDNS if (!m_configData.value(amnezia::config_key::dns1).toString(). contains(amnezia::protocols::dns::amneziaDnsIp)) { dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString())); } #ifdef Q_OS_WIN QThread::msleep(8000); #endif #ifdef Q_OS_MACOS QThread::msleep(5000); IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr); IpcClient::Interface()->updateResolvers("utun22", dnsAddr); #endif #ifdef Q_OS_LINUX QThread::msleep(1000); IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr); IpcClient::Interface()->updateResolvers("tun2", dnsAddr); #endif #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) // killSwitch toggle if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { m_configData.insert("vpnServer", m_remoteAddress); IpcClient::Interface()->enableKillSwitch(m_configData, 0); } #endif if (m_routeMode == Settings::RouteMode::VpnAllSites) { IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1"); IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress); } IpcClient::Interface()->StopRoutingIpv6(); #ifdef Q_OS_WIN IpcClient::Interface()->updateResolvers("tun2", dnsAddr); QList netInterfaces = QNetworkInterface::allInterfaces(); for (int i = 0; i < netInterfaces.size(); i++) { for (int j = 0; j < netInterfaces.at(i).addressEntries().size(); j++) { // killSwitch toggle if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index()); } m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnGateway", m_vpnGateway); m_configData.insert("vpnServer", m_remoteAddress); IpcClient::Interface()->enablePeerTraffic(m_configData); } } } #endif setConnectionState(Vpn::ConnectionState::Connected); } #if !defined(Q_OS_MACOS) if (vpnState == Vpn::ConnectionState::Disconnected) { setConnectionState(Vpn::ConnectionState::Disconnected); IpcClient::Interface()->deleteTun("tun2"); IpcClient::Interface()->StartRoutingIpv6(); IpcClient::Interface()->clearSavedRoutes(); } #endif }); return ErrorCode::NoError; } void XrayProtocol::stop() { #if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS) IpcClient::Interface()->disableKillSwitch(); IpcClient::Interface()->StartRoutingIpv6(); #endif qDebug() << "XrayProtocol::stop()"; m_xrayProcess.disconnect(); m_xrayProcess.kill(); m_xrayProcess.waitForFinished(3000); if (m_t2sProcess) { m_t2sProcess->stop(); } setConnectionState(Vpn::ConnectionState::Disconnected); } QString XrayProtocol::xrayExecPath() { #ifdef Q_OS_WIN return Utils::executable(QString("xray/xray"), true); #else return Utils::executable(QString("xray"), true); #endif } void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration) { m_configData = configuration; QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject(); if (xrayConfiguration.isEmpty()) { xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject(); } m_xrayConfig = xrayConfiguration; m_localPort = QString(amnezia::protocols::xray::defaultLocalProxyPort).toInt(); m_remoteHost = configuration.value(amnezia::config_key::hostName).toString(); m_remoteAddress = NetworkUtilities::getIPAddress(m_remoteHost); m_routeMode = static_cast(configuration.value(amnezia::config_key::splitTunnelType).toInt()); m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString(); m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString(); }