diff --git a/src/interfaces/node.h b/src/interfaces/node.h index f6c79f0c1b..3cd51dc523 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -22,6 +22,9 @@ #include #include +static const char DEFAULT_PROXY_HOST[] = "127.0.0.1"; +static constexpr uint16_t DEFAULT_PROXY_PORT = 9050; + class BanMan; class CFeeRate; class CNodeStats; @@ -268,6 +271,8 @@ class Node //! accessible across processes. virtual node::NodeContext* context() { return nullptr; } virtual void setContext(node::NodeContext* context) { } + virtual bool validateProxyAddress(const std::string ipAddress) = 0; + virtual std::string defaultProxyAddress() = 0; }; //! Return implementation of Node interface. diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index f1fe42206e..a55efe23b1 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -398,6 +399,21 @@ class NodeImpl : public Node ArgsManager& args() { return *Assert(Assert(m_context)->args); } ChainstateManager& chainman() { return *Assert(m_context->chainman); } NodeContext* m_context{nullptr}; + bool validateProxyAddress(const std::string ipAddress) override + { + uint16_t port{0}; + std::string hostname; + if (!SplitHostPort(ipAddress, port, hostname) || !port) return false; + + CService serv(LookupNumeric(ipAddress, DEFAULT_PROXY_PORT)); + + Proxy addrProxy = Proxy(serv, true); + return addrProxy.IsValid(); + } + std::string defaultProxyAddress() override + { + return std::string(DEFAULT_PROXY_HOST) + ":" + ToString(DEFAULT_PROXY_PORT); + } }; bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock& lock, const CChain& active, const BlockManager& blockman) diff --git a/src/qml/components/ProxySettings.qml b/src/qml/components/ProxySettings.qml index bc1467d0ff..753c33a3e8 100644 --- a/src/qml/components/ProxySettings.qml +++ b/src/qml/components/ProxySettings.qml @@ -7,6 +7,8 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import "../controls" +import org.bitcoincore.qt 1.0 + ColumnLayout { spacing: 4 Header { @@ -42,13 +44,16 @@ ColumnLayout { id: defaultProxy Layout.fillWidth: true header: qsTr("IP and Port") - errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.") + errorText: qsTr("Invalid IP address or port format.") state: !defaultProxyEnable.loadedItem.checked ? "DISABLED" : "FILLED" showErrorText: !defaultProxy.loadedItem.validInput && defaultProxyEnable.loadedItem.checked actionItem: IPAddressValueInput { parentState: defaultProxy.state - description: "127.0.0.1:9050" + description: nodeModel.defaultProxyAddress() activeFocusOnTab: true + onTextChanged: { + validInput = nodeModel.validateProxyAddress(text); + } } onClicked: { loadedItem.filled = true @@ -95,8 +100,11 @@ ColumnLayout { showErrorText: !torProxy.loadedItem.validInput && torProxyEnable.loadedItem.checked actionItem: IPAddressValueInput { parentState: torProxy.state - description: "127.0.0.1:9050" + description: nodeModel.defaultProxyAddress() activeFocusOnTab: true + onTextChanged: { + validInput = nodeModel.validateProxyAddress(text); + } } onClicked: { loadedItem.filled = true diff --git a/src/qml/controls/IPAddressValueInput.qml b/src/qml/controls/IPAddressValueInput.qml index d2ce4c7f16..3a70ee990c 100644 --- a/src/qml/controls/IPAddressValueInput.qml +++ b/src/qml/controls/IPAddressValueInput.qml @@ -16,7 +16,7 @@ TextInput { property bool validInput: true enabled: true state: root.parentState - validator: RegExpValidator { regExp: /[0-9.:]*/ } // Allow only digits, dots, and colons + validator: RegExpValidator { regExp: /[0-9A-Fa-f.:\[\]]*/ } maximumLength: 21 @@ -53,30 +53,4 @@ TextInput { Behavior on color { ColorAnimation { duration: 150 } } - - function isValidIPPort(input) - { - var parts = input.split(":"); - if (parts.length !== 2) return false; - if (parts[1].length === 0) return false; // port part is empty - var ipAddress = parts[0]; - var ipAddressParts = ipAddress.split("."); - if (ipAddressParts.length !== 4) return false; - for (var i = 0; (i < ipAddressParts.length); i++) { - if (ipAddressParts[i].length === 0) return false; // ip group number part is empty - if (parseInt(ipAddressParts[i]) > 255) return false; - } - var port = parseInt(parts[1]); - if (port < 1 || port > 65535) return false; - return true; - } - - // Connections element to ensure validation on editing finished - Connections { - target: root - function onTextChanged() { - // Validate the input whenever editing is finished - validInput = isValidIPPort(root.text); - } - } } diff --git a/src/qml/models/nodemodel.cpp b/src/qml/models/nodemodel.cpp index 521e5fa1c5..319f4fac03 100644 --- a/src/qml/models/nodemodel.cpp +++ b/src/qml/models/nodemodel.cpp @@ -166,3 +166,13 @@ void NodeModel::ConnectToNumConnectionsChangedSignal() setNumOutboundPeers(new_num_peers.outbound_full_relay + new_num_peers.block_relay); }); } + +bool NodeModel::validateProxyAddress(QString ipAddress) +{ + return m_node.validateProxyAddress(ipAddress.toStdString()); +} + +QString NodeModel::defaultProxyAddress() +{ + return QString::fromStdString(m_node.defaultProxyAddress()); +} diff --git a/src/qml/models/nodemodel.h b/src/qml/models/nodemodel.h index a17f9b0833..791c9e9f68 100644 --- a/src/qml/models/nodemodel.h +++ b/src/qml/models/nodemodel.h @@ -62,6 +62,9 @@ class NodeModel : public QObject void startShutdownPolling(); void stopShutdownPolling(); + Q_INVOKABLE bool validateProxyAddress(QString ipAddress); + Q_INVOKABLE QString defaultProxyAddress(); + public Q_SLOTS: void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);