From 96b336404dcdf8c710e9a510d90e15816be99f5c Mon Sep 17 00:00:00 2001 From: askmeaboutloom Date: Sat, 17 Aug 2024 16:03:16 +0200 Subject: [PATCH] Allow disabling network proxies Both manually and automatically. There's now a setting in the network preferences to toggle between the system-defined proxy and disabling it altogether. When the system-defined proxy is enabled, Drawpile will check if it's valid at all and automatically disable it if not, e.g. because it's a proxy that can't actually tunnel connections. Relates to #1360. --- ChangeLog | 1 + .../dialogs/settingsdialog/network.cpp | 9 +++++++++ src/desktop/mainwindow.cpp | 12 +++++++++--- src/libclient/net/client.cpp | 4 ++-- src/libclient/net/client.h | 5 +++-- src/libclient/net/server.cpp | 11 ++++++----- src/libclient/net/server.h | 3 ++- src/libclient/net/tcpserver.cpp | 7 ++++++- src/libclient/net/tcpserver.h | 2 +- src/libclient/net/websocketserver.cpp | 13 ++++++++++++- src/libclient/net/websocketserver.h | 3 ++- src/libclient/server/builtinserver.cpp | 9 +++++++-- src/libclient/server/builtinserver.h | 4 ++-- src/libclient/settings_table.h | 1 + src/libshared/CMakeLists.txt | 2 ++ src/libshared/net/proxy.h | 19 +++++++++++++++++++ 16 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 src/libshared/net/proxy.h diff --git a/ChangeLog b/ChangeLog index eb488a078..75cea0627 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Unreleased Version 2.2.2-pre * Fix: Solve rendering glitches with selection outlines that happen on some systems. Thanks xxxx for reporting. * Feature: Allow scaling animation exports. Thanks Hopfel for animating across a giant canvas. + * Fix: Allow disabling the application proxy in the network preferences and automatically detect bad proxy configurations that can't actually make connections. Thanks FishEggsThe for reporting. 2024-08-09 Version 2.2.2-beta.3 * Fix: Use more accurate timers for performance profiles if the platform supports it. diff --git a/src/desktop/dialogs/settingsdialog/network.cpp b/src/desktop/dialogs/settingsdialog/network.cpp index 39a3822f7..849adc42c 100644 --- a/src/desktop/dialogs/settingsdialog/network.cpp +++ b/src/desktop/dialogs/settingsdialog/network.cpp @@ -9,6 +9,7 @@ #include "desktop/widgets/kis_slider_spin_box.h" #include "libclient/utils/avatarlistmodel.h" #include "libclient/utils/avatarlistmodeldelegate.h" +#include "libshared/net/proxy.h" #include #include #include @@ -138,6 +139,14 @@ void Network::initNetwork( form->addRow( tr("Network timeout:"), utils::encapsulate(tr("%1 seconds"), timeout)); +#ifndef __EMSCRIPTEN__ + auto *proxy = utils::addRadioGroup( + form, tr("Network proxy:"), true, + {{tr("System"), int(net::ProxyMode::Default)}, + {tr("Disabled"), int(net::ProxyMode::Disabled)}}); + settings.bindNetworkProxyMode(proxy); +#endif + auto *messageQueueDrainRate = new KisSliderSpinBox; messageQueueDrainRate->setRange( 0, net::MessageQueue::MAX_SMOOTH_DRAIN_RATE); diff --git a/src/desktop/mainwindow.cpp b/src/desktop/mainwindow.cpp index b9e2cce18..1c1eb0cd9 100644 --- a/src/desktop/mainwindow.cpp +++ b/src/desktop/mainwindow.cpp @@ -2272,6 +2272,7 @@ void MainWindow::hostSession( return; } + // clang-format on // Start server if hosting locally const desktop::settings::Settings &settings = dpApp().settings(); if(!useremote) { @@ -2283,7 +2284,8 @@ void MainWindow::hostSession( QString errorMessage; bool serverStarted = server->start( settings.serverPort(), settings.serverTimeout(), - settings.serverPrivateUserList(), &errorMessage); + settings.networkProxyMode(), settings.serverPrivateUserList(), + &errorMessage); if(!serverStarted) { QMessageBox::warning(this, tr("Host Session"), errorMessage); delete server; @@ -2303,6 +2305,7 @@ void MainWindow::hostSession( return; #endif } + // clang-format off // Connect to server net::LoginHandler *login = new net::LoginHandler( @@ -2326,7 +2329,8 @@ void MainWindow::hostSession( shouldShowDialogMaximized()); m_doc->client()->connectToServer( - settings.serverTimeout(), login, !useremote); + settings.serverTimeout(), settings.networkProxyMode(), login, + !useremote); } void MainWindow::invite() @@ -2551,7 +2555,9 @@ void MainWindow::joinSession(const QUrl& url, const QString &autoRecordFile) dlg->show(); m_doc->setRecordOnConnect(autoRecordFile); - m_doc->client()->connectToServer(dpApp().settings().serverTimeout(), login, false); + const desktop::settings::Settings &settings = dpApp().settings(); + m_doc->client()->connectToServer( + settings.serverTimeout(), settings.networkProxyMode(), login, false); } /** diff --git a/src/libclient/net/client.cpp b/src/libclient/net/client.cpp index c51fc20b6..f5535211c 100644 --- a/src/libclient/net/client.cpp +++ b/src/libclient/net/client.cpp @@ -26,12 +26,12 @@ Client::Client(CommandHandler *commandHandler, QObject *parent) } void Client::connectToServer( - int timeoutSecs, LoginHandler *loginhandler, bool builtin) + int timeoutSecs, int proxyMode, LoginHandler *loginhandler, bool builtin) { Q_ASSERT(!isConnected()); m_builtin = builtin; - m_server = Server::make(loginhandler->url(), timeoutSecs, this); + m_server = Server::make(loginhandler->url(), timeoutSecs, proxyMode, this); m_server->setSmoothDrainRate(m_smoothDrainRate); #ifdef Q_OS_ANDROID diff --git a/src/libclient/net/client.h b/src/libclient/net/client.h index 3cb225a1c..d0f82f21b 100644 --- a/src/libclient/net/client.h +++ b/src/libclient/net/client.h @@ -51,8 +51,9 @@ class Client final : public QObject { * @brief Connect to a remote server * @param loginhandler the login handler to use */ - void - connectToServer(int timeoutSecs, LoginHandler *loginhandler, bool builtin); + void connectToServer( + int timeoutSecs, int proxyMode, LoginHandler *loginhandler, + bool builtin); /** * @brief Disconnect from the remote server diff --git a/src/libclient/net/server.cpp b/src/libclient/net/server.cpp index 0f079e998..6ed9cbdbc 100644 --- a/src/libclient/net/server.cpp +++ b/src/libclient/net/server.cpp @@ -15,20 +15,21 @@ namespace net { -Server *Server::make(const QUrl &url, int timeoutSecs, Client *client) +Server *Server::make( + const QUrl &url, int timeoutSecs, int proxyMode, Client *client) { #if defined(HAVE_WEBSOCKETS) && defined(HAVE_TCPSOCKETS) if(url.scheme().startsWith(QStringLiteral("ws"), Qt::CaseInsensitive)) { - return new WebSocketServer(timeoutSecs, client); + return new WebSocketServer(timeoutSecs, proxyMode, client); } else { - return new TcpServer(timeoutSecs, client); + return new TcpServer(timeoutSecs, proxyMode, client); } #elif defined(HAVE_TCPSOCKETS) Q_UNUSED(url); - return new TcpServer(timeoutSecs, client); + return new TcpServer(timeoutSecs, proxyMode, client); #elif defined(HAVE_WEBSOCKETS) Q_UNUSED(url); - return new WebSocketServer(timeoutSecs, client); + return new WebSocketServer(timeoutSecs, proxyMode, client); #else # error "No socket implementation compiled in." #endif diff --git a/src/libclient/net/server.h b/src/libclient/net/server.h index 75a0b08fe..65ed09ac6 100644 --- a/src/libclient/net/server.h +++ b/src/libclient/net/server.h @@ -31,7 +31,8 @@ class Server : public QObject { TRUSTED_HOST // A host we have explicitly marked as trusted }; - static Server *make(const QUrl &url, int timeoutSecs, Client *client); + static Server * + make(const QUrl &url, int timeoutSecs, int proxyMode, Client *client); static QString addSchemeToUserSuppliedAddress(const QString &remoteAddress); static QUrl fixUpAddress(const QUrl &originalUrl, bool join); diff --git a/src/libclient/net/tcpserver.cpp b/src/libclient/net/tcpserver.cpp index b1f908597..0c1cc15eb 100644 --- a/src/libclient/net/tcpserver.cpp +++ b/src/libclient/net/tcpserver.cpp @@ -2,18 +2,23 @@ #include "libclient/net/tcpserver.h" #include "cmake-config/config.h" #include "libclient/net/login.h" +#include "libshared/net/proxy.h" #include "libshared/net/tcpmessagequeue.h" #include "libshared/util/qtcompat.h" #include +#include #include #include namespace net { -TcpServer::TcpServer(int timeoutSecs, Client *client) +TcpServer::TcpServer(int timeoutSecs, int proxyMode, Client *client) : Server(client) { m_socket = new QSslSocket(this); + if(shouldDisableProxy(proxyMode, m_socket->proxy(), false, false)) { + m_socket->setProxy(QNetworkProxy::NoProxy); + } m_socket->setSocketOption(QAbstractSocket::LowDelayOption, true); QSslConfiguration sslconf = m_socket->sslConfiguration(); diff --git a/src/libclient/net/tcpserver.h b/src/libclient/net/tcpserver.h index 6c00fab5b..5c3c82685 100644 --- a/src/libclient/net/tcpserver.h +++ b/src/libclient/net/tcpserver.h @@ -12,7 +12,7 @@ namespace net { class TcpServer final : public Server { Q_OBJECT public: - explicit TcpServer(int timeoutSecs, Client *client); + explicit TcpServer(int timeoutSecs, int proxyMode, Client *client); bool isWebSocket() const override; diff --git a/src/libclient/net/websocketserver.cpp b/src/libclient/net/websocketserver.cpp index d3e28bc50..9fd3086fa 100644 --- a/src/libclient/net/websocketserver.cpp +++ b/src/libclient/net/websocketserver.cpp @@ -2,6 +2,10 @@ #include "libclient/net/websocketserver.h" #include "libshared/net/websocketmessagequeue.h" #include +#ifndef __EMSCRIPTEN__ +# include "libshared/net/proxy.h" +# include +#endif namespace { #ifdef HAVE_WEBSOCKETS @@ -16,10 +20,17 @@ const auto WebSocketError = namespace net { -WebSocketServer::WebSocketServer(int timeoutSecs, Client *client) +WebSocketServer::WebSocketServer(int timeoutSecs, int proxyMode, Client *client) : Server(client) { m_socket = new QWebSocket(QString(), QWebSocketProtocol::Version13, this); +#ifdef __EMSCRIPTEN__ + Q_UNUSED(proxyMode); +#else + if(shouldDisableProxy(proxyMode, m_socket->proxy(), true, false)) { + m_socket->setProxy(QNetworkProxy::NoProxy); + } +#endif m_msgqueue = new WebSocketMessageQueue(m_socket, true, this); m_msgqueue->setIdleTimeout(timeoutSecs * 1000); diff --git a/src/libclient/net/websocketserver.h b/src/libclient/net/websocketserver.h index 1e6e36f8d..2e4824bbc 100644 --- a/src/libclient/net/websocketserver.h +++ b/src/libclient/net/websocketserver.h @@ -12,7 +12,8 @@ class WebSocketMessageQueue; class WebSocketServer final : public Server { Q_OBJECT public: - explicit WebSocketServer(int timeoutSecs, Client *client); + explicit WebSocketServer( + int timeoutSecs, int proxyMode, Client *client); bool isWebSocket() const override; diff --git a/src/libclient/server/builtinserver.cpp b/src/libclient/server/builtinserver.cpp index b34eacb85..0ff2e4fd4 100644 --- a/src/libclient/server/builtinserver.cpp +++ b/src/libclient/server/builtinserver.cpp @@ -6,7 +6,9 @@ #include "libserver/client.h" #include "libserver/inmemoryconfig.h" #include "libserver/loginhandler.h" +#include "libshared/net/proxy.h" #include +#include #include namespace server { @@ -60,12 +62,15 @@ std::tuple BuiltinServer::createSession( } bool BuiltinServer::start( - quint16 preferredPort, int clientTimeout, bool privateUserList, - QString *outErrorMessage) + quint16 preferredPort, int clientTimeout, int proxyMode, + bool privateUserList, QString *outErrorMessage) { Q_ASSERT(!m_server); m_server = new QTcpServer{this}; + if(net::shouldDisableProxy(proxyMode, m_server->proxy(), false, true)) { + m_server->setProxy(QNetworkProxy::NoProxy); + } connect( m_server, &QTcpServer::newConnection, this, &BuiltinServer::newClient); diff --git a/src/libclient/server/builtinserver.h b/src/libclient/server/builtinserver.h index eef449a3e..ae771c027 100644 --- a/src/libclient/server/builtinserver.h +++ b/src/libclient/server/builtinserver.h @@ -50,8 +50,8 @@ class BuiltinServer final : public QObject, public Sessions { const QString &founder) override; bool start( - quint16 preferredPort, int clientTimeout, bool privateUserList, - QString *outErrorMessage = nullptr); + quint16 preferredPort, int clientTimeout, int proxyMode, + bool privateUserList, QString *outErrorMessage = nullptr); public slots: void stop(); diff --git a/src/libclient/settings_table.h b/src/libclient/settings_table.h index 7adc253c1..15edfd40e 100644 --- a/src/libclient/settings_table.h +++ b/src/libclient/settings_table.h @@ -17,6 +17,7 @@ SETTING(checkerColor2 , CheckerColor2 , "settings/ch SETTING(interpolateInputs , InterpolateInputs , "settings/input/interpolate" , true) SETTING(messageQueueDrainRate , MessageQueueDrainRate , "settings/messagequeuedrainrate" , net::MessageQueue::DEFAULT_SMOOTH_DRAIN_RATE) SETTING(mouseSmoothing , MouseSmoothing , "settings/input/mousesmoothing" , false) +SETTING(networkProxyMode , NetworkProxyMode , "settings/networkproxymode" , 0) SETTING(parentalControlsAutoTag , ParentalControlsAutoTag , "pc/autotag" , true) SETTING(parentalControlsForceCensor , ParentalControlsForceCensor , "pc/noUncensoring" , false) SETTING(parentalControlsLevel , ParentalControlsLevel , "pc/level" , parentalcontrols::Level::Unrestricted) diff --git a/src/libshared/CMakeLists.txt b/src/libshared/CMakeLists.txt index acd6885df..e8e17b999 100644 --- a/src/libshared/CMakeLists.txt +++ b/src/libshared/CMakeLists.txt @@ -13,6 +13,8 @@ target_sources(dpshared PRIVATE net/messagequeue.h net/protover.cpp net/protover.h + net/proxy.cpp + net/proxy.h net/servercmd.cpp net/servercmd.h util/database.cpp diff --git a/src/libshared/net/proxy.h b/src/libshared/net/proxy.h new file mode 100644 index 000000000..559ff6b03 --- /dev/null +++ b/src/libshared/net/proxy.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#ifndef LIBSHARED_NET_PROXY_H +#define LIBSHARED_NET_PROXY_H + +class QNetworkProxy; + +namespace net { + +enum class ProxyMode { + Default = 0, + Disabled = 1, +}; + +bool shouldDisableProxy( + int proxyMode, const QNetworkProxy &proxy, bool allowHttp, bool listen); + +} + +#endif