mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 10:50:23 +08:00
319 lines
12 KiB
C++
319 lines
12 KiB
C++
#include "QvProxyConfigurator.hpp"
|
|
#include "common/QvHelpers.hpp"
|
|
#ifdef Q_OS_WIN
|
|
#include "wininet.h"
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
namespace Qv2ray::components::proxy
|
|
{
|
|
|
|
#ifdef Q_OS_MACOS
|
|
QStringList macOSgetNetworkServices()
|
|
{
|
|
QProcess p;
|
|
p.setProgram("/usr/sbin/networksetup");
|
|
p.setArguments(QStringList() << "-listallnetworkservices");
|
|
p.start();
|
|
p.waitForStarted();
|
|
p.waitForFinished();
|
|
LOG(PROXY, p.errorString())
|
|
auto str = p.readAllStandardOutput();
|
|
auto lines = SplitLines(str);
|
|
QStringList result;
|
|
|
|
// Start from 1 since first line is unneeded.
|
|
for (auto i = 1; i < lines.count(); i++) {
|
|
// * means disabled.
|
|
if (!lines[i].contains("*")) {
|
|
result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]);
|
|
}
|
|
}
|
|
|
|
LOG(PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
|
|
return result;
|
|
}
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
#define NO_CONST(expr) const_cast<wchar_t *>(expr)
|
|
//static auto DEFAULT_CONNECTION_NAME = NO_CONST(L"DefaultConnectionSettings");
|
|
///
|
|
/// INTERNAL FUNCTION
|
|
bool __QueryProxyOptions()
|
|
{
|
|
INTERNET_PER_CONN_OPTION_LIST List;
|
|
INTERNET_PER_CONN_OPTION Option[5];
|
|
//
|
|
unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
|
Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
|
Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
|
|
Option[2].dwOption = INTERNET_PER_CONN_FLAGS;
|
|
Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
|
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
|
//
|
|
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
|
List.pszConnection = nullptr;// NO_CONST(DEFAULT_CONNECTION_NAME);
|
|
List.dwOptionCount = 5;
|
|
List.dwOptionError = 0;
|
|
List.pOptions = Option;
|
|
|
|
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
|
LOG(PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError()))
|
|
}
|
|
|
|
LOG(PROXY, "System default proxy info:")
|
|
|
|
if (Option[0].Value.pszValue != nullptr) {
|
|
LOG(PROXY, QString::fromWCharArray(Option[0].Value.pszValue))
|
|
}
|
|
|
|
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) {
|
|
LOG(PROXY, "PROXY_TYPE_AUTO_PROXY_URL")
|
|
}
|
|
|
|
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) {
|
|
LOG(PROXY, "PROXY_TYPE_AUTO_DETECT")
|
|
}
|
|
|
|
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) {
|
|
LOG(PROXY, "PROXY_TYPE_DIRECT")
|
|
}
|
|
|
|
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) {
|
|
LOG(PROXY, "PROXY_TYPE_PROXY")
|
|
}
|
|
|
|
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
|
LOG(PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError()))
|
|
}
|
|
|
|
if (Option[4].Value.pszValue != nullptr) {
|
|
LOG(PROXY, QString::fromStdWString(Option[4].Value.pszValue))
|
|
}
|
|
|
|
INTERNET_VERSION_INFO Version;
|
|
nSize = sizeof(INTERNET_VERSION_INFO);
|
|
InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize);
|
|
|
|
if (Option[0].Value.pszValue != nullptr) {
|
|
GlobalFree(Option[0].Value.pszValue);
|
|
}
|
|
|
|
if (Option[3].Value.pszValue != nullptr) {
|
|
GlobalFree(Option[3].Value.pszValue);
|
|
}
|
|
|
|
if (Option[4].Value.pszValue != nullptr) {
|
|
GlobalFree(Option[4].Value.pszValue);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC)
|
|
{
|
|
INTERNET_PER_CONN_OPTION_LIST list;
|
|
BOOL bReturn;
|
|
DWORD dwBufSize = sizeof(list);
|
|
// Fill the list structure.
|
|
list.dwSize = sizeof(list);
|
|
// NULL == LAN, otherwise connectoid name.
|
|
list.pszConnection = nullptr;
|
|
|
|
if (isPAC) {
|
|
LOG(PROXY, "Setting system proxy for PAC")
|
|
//
|
|
list.dwOptionCount = 2;
|
|
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
|
|
|
|
// Ensure that the memory was allocated.
|
|
if (nullptr == list.pOptions) {
|
|
// Return FALSE if the memory wasn't allocated.
|
|
return FALSE;
|
|
}
|
|
|
|
// Set flags.
|
|
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
|
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL;
|
|
// Set proxy name.
|
|
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
|
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
|
} else {
|
|
LOG(PROXY, "Setting system proxy for Global Proxy")
|
|
//
|
|
list.dwOptionCount = 3;
|
|
list.pOptions = new INTERNET_PER_CONN_OPTION[3];
|
|
|
|
if (nullptr == list.pOptions) {
|
|
return false;
|
|
}
|
|
|
|
// Set flags.
|
|
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
|
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
|
|
// Set proxy name.
|
|
list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
|
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
|
// Set proxy override.
|
|
list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
|
auto localhost = L"localhost";
|
|
list.pOptions[2].Value.pszValue = NO_CONST(localhost);
|
|
}
|
|
|
|
// Set the options on the connection.
|
|
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
|
delete [] list.pOptions;
|
|
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
|
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
|
return bReturn;
|
|
}
|
|
#endif
|
|
|
|
void SetSystemProxy(const QString &address, int httpPort, int socksPort, bool usePAC)
|
|
{
|
|
bool hasHTTP = (httpPort != 0);
|
|
bool hasSOCKS = (socksPort != 0);
|
|
|
|
if (!(hasHTTP || hasSOCKS || usePAC)) {
|
|
LOG(PROXY, "Nothing?")
|
|
return;
|
|
}
|
|
|
|
if (usePAC) {
|
|
LOG(PROXY, "Qv2ray will set system proxy to use PAC file")
|
|
} else {
|
|
if (hasHTTP) {
|
|
LOG(PROXY, "Qv2ray will set system proxy to use HTTP")
|
|
}
|
|
|
|
if (hasSOCKS) {
|
|
LOG(PROXY, "Qv2ray will set system proxy to use SOCKS")
|
|
}
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
QString __a;
|
|
|
|
if (usePAC) {
|
|
__a = address;
|
|
} else {
|
|
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
|
|
}
|
|
|
|
LOG(PROXY, "Windows proxy string: " + __a)
|
|
auto proxyStrW = new WCHAR[__a.length() + 1];
|
|
wcscpy(proxyStrW, __a.toStdWString().c_str());
|
|
//
|
|
__QueryProxyOptions();
|
|
|
|
if (!__SetProxyOptions(proxyStrW, usePAC)) {
|
|
LOG(PROXY, "Failed to set proxy.")
|
|
}
|
|
|
|
__QueryProxyOptions();
|
|
#elif defined(Q_OS_LINUX)
|
|
QStringList actions;
|
|
auto proxyMode = usePAC ? "auto" : "manual";
|
|
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
|
|
|
|
if (usePAC) {
|
|
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address);
|
|
} else {
|
|
if (hasHTTP) {
|
|
actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address);
|
|
actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort);
|
|
//
|
|
actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address);
|
|
actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);;
|
|
}
|
|
|
|
if (hasSOCKS) {
|
|
actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address);
|
|
actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort);
|
|
}
|
|
}
|
|
|
|
// note: do not use std::all_of / any_of / none_of,
|
|
// because those are short-circuit and cannot guarantee atomicity.
|
|
auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString & action) {
|
|
DEBUG(PROXY, action)
|
|
return QProcess::execute(action) == QProcess::NormalExit;
|
|
}) == actions.size();
|
|
|
|
if (!result) {
|
|
LOG(PROXY, "Something wrong happens when setting system proxy -> Gnome ONLY.")
|
|
LOG(PROXY, "If you are using KDE Plasma and receiving this message, just simply ignore this.")
|
|
}
|
|
|
|
Q_UNUSED(result);
|
|
#else
|
|
|
|
for (auto service : macOSgetNetworkServices()) {
|
|
LOG(PROXY, "Setting proxy for interface: " + service)
|
|
|
|
if (usePAC) {
|
|
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on");
|
|
QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address);
|
|
} else {
|
|
if (hasHTTP) {
|
|
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
|
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on");
|
|
QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
|
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
|
}
|
|
|
|
if (hasSOCKS) {
|
|
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
|
|
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void ClearSystemProxy()
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
LOG(PROXY, "Cleaning system proxy settings.")
|
|
INTERNET_PER_CONN_OPTION_LIST list;
|
|
BOOL bReturn;
|
|
DWORD dwBufSize = sizeof(list);
|
|
// Fill out list struct.
|
|
list.dwSize = sizeof(list);
|
|
// nullptr == LAN, otherwise connectoid name.
|
|
list.pszConnection = nullptr;
|
|
// Set three options.
|
|
list.dwOptionCount = 1;
|
|
list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount];
|
|
|
|
// Make sure the memory was allocated.
|
|
if (nullptr == list.pOptions) {
|
|
// Return FALSE if the memory wasn't allocated.
|
|
LOG(PROXY, "Failed to allocat memory in DisableConnectionProxy()")
|
|
}
|
|
|
|
// Set flags.
|
|
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
|
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
|
|
//
|
|
// Set the options on the connection.
|
|
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
|
delete [] list.pOptions;
|
|
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
|
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
|
#elif defined(Q_OS_LINUX)
|
|
QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'");
|
|
#else
|
|
|
|
for (auto service : macOSgetNetworkServices()) {
|
|
LOG(PROXY, "Clearing proxy for interface: " + service)
|
|
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off");
|
|
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off");
|
|
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " off");
|
|
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " off");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
}
|