From 8200ef6bd5aaeb142b112d11d758155c856e8350 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Fri, 6 Dec 2019 02:24:49 +0200 Subject: [PATCH] Rework the listening IP/interface selection code Closes #11561 --- src/base/bittorrent/session.cpp | 242 ++++++++++--------- src/base/bittorrent/session.h | 4 +- src/base/settingsstorage.cpp | 1 - src/base/utils/net.cpp | 32 +++ src/base/utils/net.h | 1 + src/gui/advancedsettings.cpp | 58 ++--- src/gui/advancedsettings.h | 2 +- src/webui/api/appcontroller.cpp | 19 +- src/webui/www/private/views/preferences.html | 2 + 9 files changed, 198 insertions(+), 163 deletions(-) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index d4c394a0a..c5781f8cf 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -436,7 +436,6 @@ Session::Session(QObject *parent) , m_networkInterface(BITTORRENT_SESSION_KEY("Interface")) , m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName")) , m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress")) - , m_isIPv6Enabled(BITTORRENT_SESSION_KEY("IPv6Enabled"), false) , m_encryption(BITTORRENT_SESSION_KEY("Encryption"), 0) , m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false) , m_chokingAlgorithm(BITTORRENT_SESSION_KEY("ChokingAlgorithm"), ChokingAlgorithm::FixedSlots @@ -1244,68 +1243,7 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack) // It will not take affect until the listen_interfaces settings is updated settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize()); -#ifdef Q_OS_WIN - QString chosenIP; -#endif - if (!m_listenInterfaceConfigured) { - const int port = useRandomPort() ? 0 : this->port(); - if (port > 0) // user specified port - settingsPack.set_int(lt::settings_pack::max_retry_port_bind, 0); - - for (const QString &ip : asConst(getListeningIPs())) { - if (ip.isEmpty()) { - const QString anyIP = QHostAddress(QHostAddress::AnyIPv4).toString(); - const std::string endpoint = anyIP.toStdString() + ':' + std::to_string(port); - settingsPack.set_str(lt::settings_pack::listen_interfaces, endpoint); - LogMsg(tr("Trying to listen on IP: %1, port: %2" - , "e.g: Trying to listen on IP: 192.168.0.1, port: 6881") - .arg(anyIP, QString::number(port)) - , Log::INFO); - break; - } - - lt::error_code ec; - const lt::address addr = lt::address::from_string(ip.toStdString(), ec); - if (!ec) { - const std::string endpoint = (addr.is_v6() - ? ('[' + addr.to_string() + ']') - : addr.to_string()) - + ':' + std::to_string(port); - settingsPack.set_str(lt::settings_pack::listen_interfaces, endpoint); - LogMsg(tr("Trying to listen on IP: %1, port: %2" - , "e.g: Trying to listen on IP: 192.168.0.1, port: 6881") - .arg(ip, QString::number(port)) - , Log::INFO); -#ifdef Q_OS_WIN - chosenIP = ip; -#endif - break; - } - } - -#ifdef Q_OS_WIN - // On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns - // the interface's LUID and not the GUID. - // Libtorrent expects GUIDs for the 'outgoing_interfaces' setting. - const QString netInterface = networkInterface(); - if (!netInterface.isEmpty()) { - const QString guid = convertIfaceNameToGuid(netInterface); - if (!guid.isEmpty()) { - settingsPack.set_str(lt::settings_pack::outgoing_interfaces, guid.toStdString()); - } - else { - settingsPack.set_str(lt::settings_pack::outgoing_interfaces, chosenIP.toStdString()); - LogMsg(tr("Could not get GUID of configured network interface. Binding to IP: %1").arg(chosenIP) - , Log::WARNING); - } - } -#else - settingsPack.set_str(lt::settings_pack::outgoing_interfaces, networkInterface().toStdString()); -#endif // Q_OS_WIN - - m_listenInterfaceConfigured = true; - } - + configureNetworkInterfaces(settingsPack); applyBandwidthLimits(settingsPack); // The most secure, rc4 only so that all streams are encrypted @@ -1496,6 +1434,75 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack) } } +void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack) +{ + if (m_listenInterfaceConfigured) + return; + + const int port = useRandomPort() ? 0 : this->port(); + if (port > 0) // user specified port + settingsPack.set_int(lt::settings_pack::max_retry_port_bind, 0); + + QStringList endpoints; + QStringList outgoingInterfaces; + const QString portString = ':' + QString::number(port); + + for (const QString &ip : asConst(getListeningIPs())) { + const QHostAddress addr {ip}; + if (!addr.isNull()) { + endpoints << ((addr.protocol() == QAbstractSocket::IPv6Protocol) + ? ('[' + Utils::Net::canonicalIPv6Addr(addr).toString() + ']') + : addr.toString()) + + portString; + } + else { + // ip holds an interface name +#ifdef Q_OS_WIN + // On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns + // the interface's LUID and not the GUID. + // Libtorrent expects GUIDs for the 'listen_interfaces' setting. + const QString guid = convertIfaceNameToGuid(ip); + if (!guid.isEmpty()) { + endpoints << (guid + portString); + outgoingInterfaces << guid; + } + else { + LogMsg(tr("Could not get GUID of network interface: %1").arg(ip) , Log::WARNING); + } +#else + endpoints << (ip + portString); + outgoingInterfaces << ip; +#endif + } + } + + if (outgoingInterfaces.isEmpty()) { +#ifdef Q_OS_WIN + // On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns + // the interface's LUID and not the GUID. + // Libtorrent expects GUIDs for the 'outgoing_interfaces' setting. + const QString netInterface = networkInterface(); + if (!netInterface.isEmpty()) { + const QString guid = convertIfaceNameToGuid(netInterface); + if (!guid.isEmpty()) + outgoingInterfaces << guid; + else + LogMsg(tr("Could not get GUID of network interface: %1").arg(netInterface) , Log::WARNING); + } +#else + outgoingInterfaces << networkInterface(); +#endif // Q_OS_WIN + } + + const QString finalEndpoints = endpoints.join(','); + settingsPack.set_str(lt::settings_pack::listen_interfaces, finalEndpoints.toStdString()); + LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881") + .arg(finalEndpoints), Log::INFO); + + settingsPack.set_str(lt::settings_pack::outgoing_interfaces, outgoingInterfaces.join(',').toStdString()); + m_listenInterfaceConfigured = true; +} + void Session::configurePeerClasses() { lt::ip_filter f; @@ -2416,22 +2423,50 @@ QStringList Session::getListeningIPs() const const QString ifaceName = networkInterface(); const QString ifaceAddr = networkInterfaceAddress(); - const bool listenIPv6 = isIPv6Enabled(); + const QHostAddress configuredAddr(ifaceAddr); + const bool allIPv4 = (ifaceAddr == QLatin1String("0.0.0.0")); // Means All IPv4 addresses + const bool allIPv6 = (ifaceAddr == QLatin1String("::")); // Means All IPv6 addresses - if (!ifaceAddr.isEmpty()) { - QHostAddress addr(ifaceAddr); - if (addr.isNull()) { - LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.1568.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL); - IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. - return IPs; - } + if (!ifaceAddr.isEmpty() && !allIPv4 && !allIPv6 && configuredAddr.isNull()) { + LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.158.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL); + IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. + return IPs; } if (ifaceName.isEmpty()) { - if (!ifaceAddr.isEmpty()) - IPs.append(ifaceAddr); - else - IPs.append(QString()); + if (ifaceAddr.isEmpty()) + return {QLatin1String("0.0.0.0"), QLatin1String("::")}; // Indicates all interfaces + all addresses (aka default) + + if (allIPv4) + return {QLatin1String("0.0.0.0")}; + + if (allIPv6) + return {QLatin1String("::")}; + } + + const auto checkAndAddIP = [allIPv4, allIPv6, &IPs](const QHostAddress &addr, const QHostAddress &match) + { + if ((allIPv4 && (addr.protocol() != QAbstractSocket::IPv4Protocol)) + || (allIPv6 && (addr.protocol() != QAbstractSocket::IPv6Protocol))) + return; + + if ((match == addr) || allIPv4 || allIPv6) + IPs.append(addr.toString()); + }; + + if (ifaceName.isEmpty()) { + const QList addresses = QNetworkInterface::allAddresses(); + for (const auto &addr : addresses) + checkAndAddIP(addr, configuredAddr); + + // At this point ifaceAddr was non-empty + // If IPs.isEmpty() it means the configured Address was not found + if (IPs.isEmpty()) { + LogMsg(tr("Can't find the configured address '%1' to listen on" + , "Can't find the configured address '192.168.1.3' to listen on") + .arg(ifaceAddr), Log::CRITICAL); + IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. + } return IPs; } @@ -2445,40 +2480,24 @@ QStringList Session::getListeningIPs() const return IPs; } - const QList addresses = networkIFace.addressEntries(); - qDebug("This network interface has %d IP addresses", addresses.size()); - QHostAddress ip; - QString ipString; - QAbstractSocket::NetworkLayerProtocol protocol; - for (const QNetworkAddressEntry &entry : addresses) { - ip = entry.ip(); - ipString = ip.toString(); - protocol = ip.protocol(); - Q_ASSERT((protocol == QAbstractSocket::IPv4Protocol) || (protocol == QAbstractSocket::IPv6Protocol)); - if ((!listenIPv6 && (protocol == QAbstractSocket::IPv6Protocol)) - || (listenIPv6 && (protocol == QAbstractSocket::IPv4Protocol))) - continue; - - // If an iface address has been defined to only allow ip's that match it to go through - if (!ifaceAddr.isEmpty()) { - if (ifaceAddr == ipString) { - IPs.append(ipString); - break; - } - } - else { - IPs.append(ipString); - } + if (ifaceAddr.isEmpty()) { + IPs.append(ifaceName); + return IPs; // On Windows calling code converts it to GUID } + const QList addresses = networkIFace.addressEntries(); + qDebug("This network interface has %d IP addresses", addresses.size()); + for (const QNetworkAddressEntry &entry : addresses) + checkAndAddIP(entry.ip(), configuredAddr); + // Make sure there is at least one IP - // At this point there was a valid network interface, with no suitable IP. - if (IPs.size() == 0) { - LogMsg(tr("qBittorrent didn't find an %1 local address to listen on" - , "qBittorrent didn't find an IPv4 local address to listen on") - .arg(listenIPv6 ? "IPv6" : "IPv4"), Log::CRITICAL); + // At this point there was an explicit interface and an explicit address set + // and the address should have been found + if (IPs.isEmpty()) { + LogMsg(tr("Can't find the configured address '%1' to listen on" + , "Can't find the configured address '192.168.1.3' to listen on") + .arg(ifaceAddr), Log::CRITICAL); IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. - return IPs; } return IPs; @@ -2734,19 +2753,6 @@ void Session::setNetworkInterfaceAddress(const QString &address) } } -bool Session::isIPv6Enabled() const -{ - return m_isIPv6Enabled; -} - -void Session::setIPv6Enabled(const bool enabled) -{ - if (enabled != isIPv6Enabled()) { - m_isIPv6Enabled = enabled; - configureListeningInterface(); - } -} - int Session::encryption() const { return m_encryption; diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 80586a954..4591f4477 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -303,8 +303,6 @@ namespace BitTorrent void setNetworkInterfaceName(const QString &name); QString networkInterfaceAddress() const; void setNetworkInterfaceAddress(const QString &address); - bool isIPv6Enabled() const; - void setIPv6Enabled(bool enabled); int encryption() const; void setEncryption(int state); bool isProxyPeerConnectionsEnabled() const; @@ -532,6 +530,7 @@ namespace BitTorrent void configureComponents(); void initializeNativeSession(); void loadLTSettings(lt::settings_pack &settingsPack); + void configureNetworkInterfaces(lt::settings_pack &settingsPack); void configurePeerClasses(); void adjustLimits(lt::settings_pack &settingsPack); void applyBandwidthLimits(lt::settings_pack &settingsPack) const; @@ -662,7 +661,6 @@ namespace BitTorrent CachedSettingValue m_networkInterface; CachedSettingValue m_networkInterfaceName; CachedSettingValue m_networkInterfaceAddress; - CachedSettingValue m_isIPv6Enabled; CachedSettingValue m_encryption; CachedSettingValue m_isProxyPeerConnectionsEnabled; CachedSettingValue m_chokingAlgorithm; diff --git a/src/base/settingsstorage.cpp b/src/base/settingsstorage.cpp index 40fcebd2a..618b05253 100644 --- a/src/base/settingsstorage.cpp +++ b/src/base/settingsstorage.cpp @@ -86,7 +86,6 @@ namespace {"BitTorrent/Session/BandwidthSchedulerEnabled", "Preferences/Scheduler/Enabled"}, {"BitTorrent/Session/Port", "Preferences/Connection/PortRangeMin"}, {"BitTorrent/Session/UseRandomPort", "Preferences/General/UseRandomPort"}, - {"BitTorrent/Session/IPv6Enabled", "Preferences/Connection/InterfaceListenIPv6"}, {"BitTorrent/Session/Interface", "Preferences/Connection/Interface"}, {"BitTorrent/Session/InterfaceName", "Preferences/Connection/InterfaceName"}, {"BitTorrent/Session/InterfaceAddress", "Preferences/Connection/InterfaceAddress"}, diff --git a/src/base/utils/net.cpp b/src/base/utils/net.cpp index fd11b3577..a92e658f5 100644 --- a/src/base/utils/net.cpp +++ b/src/base/utils/net.cpp @@ -28,6 +28,7 @@ #include "net.h" +#include #include #include #include @@ -91,6 +92,37 @@ namespace Utils return subnet.first.toString() + '/' + QString::number(subnet.second); } + QHostAddress canonicalIPv6Addr(const QHostAddress &addr) + { + // Link-local IPv6 textual address always contains a scope id (or zone index) + // The scope id is appended to the IPv6 address using the '%' character + // The scope id can be either a interface name or an interface number + // Examples: + // fe80::1%ethernet_17 + // fe80::1%13 + // The interface number is the mandatory supported way + // Unfortunately for us QHostAddress::toString() outputs (at least on Windows) + // the interface name, and libtorrent/boost.asio only support an interface number + // as scope id. Furthermore, QHostAddress doesn't have any convenient method to + // affect this, so we jump through hoops here. + if (addr.protocol() != QAbstractSocket::IPv6Protocol) + return QHostAddress{addr.toIPv6Address()}; + + // QHostAddress::setScopeId(addr.scopeId()); // Even though the docs say that setScopeId + // will convert a name to a number, this doesn't happen. Probably a Qt bug. + const QString scopeIdTxt = addr.scopeId(); + if (scopeIdTxt.isEmpty()) + return addr; + + const int id = QNetworkInterface::interfaceIndexFromName(scopeIdTxt); + if (id == 0) // This failure might mean that the scope id was already a number + return addr; + + QHostAddress canonical(addr.toIPv6Address()); + canonical.setScopeId(QString::number(id)); + return canonical; + } + QList loadSSLCertificate(const QByteArray &data) { const QList certs {QSslCertificate::fromData(data)}; diff --git a/src/base/utils/net.h b/src/base/utils/net.h index ee2fa2f51..e5ceb4074 100644 --- a/src/base/utils/net.h +++ b/src/base/utils/net.h @@ -50,6 +50,7 @@ namespace Utils bool isLoopbackAddress(const QHostAddress &addr); bool isIPInRange(const QHostAddress &addr, const QVector &subnets); QString subnetToString(const Subnet &subnet); + QHostAddress canonicalIPv6Addr(const QHostAddress &addr); const int MAX_SSL_FILE_SIZE = 1024 * 1024; QList loadSSLCertificate(const QByteArray &data); diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index b36a7360f..22defdc40 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -68,7 +68,6 @@ enum AdvSettingsRows NETWORK_IFACE, //Optional network address NETWORK_IFACE_ADDRESS, - NETWORK_LISTEN_IPV6, // behavior SAVE_RESUME_DATA_INTERVAL, CONFIRM_RECHECK_TORRENT, @@ -227,18 +226,14 @@ void AdvancedSettings::saveAdvancedSettings() } // Interface address - if (m_comboBoxInterfaceAddress.currentIndex() == 0) { - // All addresses (default) - session->setNetworkInterfaceAddress({}); - } - else { - QHostAddress ifaceAddr(m_comboBoxInterfaceAddress.currentText().trimmed()); - ifaceAddr.isNull() ? session->setNetworkInterfaceAddress({}) : session->setNetworkInterfaceAddress(ifaceAddr.toString()); - } - session->setIPv6Enabled(m_checkBoxListenIPv6.isChecked()); + // Construct a QHostAddress to filter malformed strings + const QHostAddress ifaceAddr(m_comboBoxInterfaceAddress.currentData().toString().trimmed()); + session->setNetworkInterfaceAddress(ifaceAddr.toString()); + // Announce IP - QHostAddress addr(m_lineEditAnnounceIP.text().trimmed()); - session->setAnnounceIP(addr.isNull() ? "" : addr.toString()); + // Construct a QHostAddress to filter malformed strings + const QHostAddress addr(m_lineEditAnnounceIP.text().trimmed()); + session->setAnnounceIP(addr.toString()); // Program notification MainWindow *const mainWindow = static_cast(QCoreApplication::instance())->mainWindow(); @@ -295,33 +290,35 @@ void AdvancedSettings::updateInterfaceAddressCombo() // Clear all items and reinsert them, default to all m_comboBoxInterfaceAddress.clear(); - m_comboBoxInterfaceAddress.addItem(tr("All addresses")); - m_comboBoxInterfaceAddress.setCurrentIndex(0); + m_comboBoxInterfaceAddress.addItem(tr("All addresses"), {}); + m_comboBoxInterfaceAddress.addItem(tr("All IPv4 addresses"), QLatin1String("0.0.0.0")); + m_comboBoxInterfaceAddress.addItem(tr("All IPv6 addresses"), QLatin1String("::")); - auto populateCombo = [this, ¤tAddress](const QString &ip, const QAbstractSocket::NetworkLayerProtocol &protocol) + const auto populateCombo = [this, ¤tAddress](const QHostAddress &addr) { - Q_ASSERT((protocol == QAbstractSocket::IPv4Protocol) || (protocol == QAbstractSocket::IPv6Protocol)); - // Only take ipv4 for now? - if ((protocol != QAbstractSocket::IPv4Protocol) && (protocol != QAbstractSocket::IPv6Protocol)) - return; - m_comboBoxInterfaceAddress.addItem(ip); - //Try to select the last added one - if (ip == currentAddress) - m_comboBoxInterfaceAddress.setCurrentIndex(m_comboBoxInterfaceAddress.count() - 1); + if (addr.protocol() == QAbstractSocket::IPv4Protocol) { + const QString str = addr.toString(); + m_comboBoxInterfaceAddress.addItem(str, str); + } + else if (addr.protocol() == QAbstractSocket::IPv6Protocol) { + const QString str = Utils::Net::canonicalIPv6Addr(addr).toString(); + m_comboBoxInterfaceAddress.addItem(str, str); + } }; if (ifaceName.isEmpty()) { - for (const QHostAddress &ip : asConst(QNetworkInterface::allAddresses())) - populateCombo(ip.toString(), ip.protocol()); + for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses())) + populateCombo(addr); } else { const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName); const QList addresses = iface.addressEntries(); - for (const QNetworkAddressEntry &entry : addresses) { - const QHostAddress ip = entry.ip(); - populateCombo(ip.toString(), ip.protocol()); - } + for (const QNetworkAddressEntry &entry : addresses) + populateCombo(entry.ip()); } + + const int index = m_comboBoxInterfaceAddress.findData(currentAddress); + m_comboBoxInterfaceAddress.setCurrentIndex(std::max(index, 0)); } void AdvancedSettings::loadAdvancedSettings() @@ -524,9 +521,6 @@ void AdvancedSettings::loadAdvancedSettings() // Network interface address updateInterfaceAddressCombo(); addRow(NETWORK_IFACE_ADDRESS, tr("Optional IP Address to bind to (requires restart)"), &m_comboBoxInterfaceAddress); - // Listen on IPv6 address - m_checkBoxListenIPv6.setChecked(session->isIPv6Enabled()); - addRow(NETWORK_LISTEN_IPV6, tr("Listen on IPv6 address (requires restart)"), &m_checkBoxListenIPv6); // Announce IP m_lineEditAnnounceIP.setText(session->announceIP()); addRow(ANNOUNCE_IP, tr("IP Address to report to trackers (requires restart)"), &m_lineEditAnnounceIP); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 502b56320..1122b58b9 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -63,7 +63,7 @@ private: m_spinBoxSendBufferWatermarkFactor, m_spinBoxSocketBacklogSize, m_spinBoxSavePathHistoryLength; QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts, m_checkBoxSuperSeeding, m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus, - m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxListenIPv6, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, + m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, m_checkBoxMultiConnectionsPerIp, m_checkBoxSuggestMode, m_checkBoxCoalesceRW, m_checkBoxSpeedWidgetEnabled; QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm, m_comboBoxSeedChokingAlgorithm; QLineEdit m_lineEditAnnounceIP; diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index a12c8519e..04d3ae4a5 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -259,8 +259,6 @@ void AppController::preferencesAction() data["current_network_interface"] = session->networkInterface(); // Current network interface address data["current_interface_address"] = BitTorrent::Session::instance()->networkInterfaceAddress(); - // Listen on IPv6 address - data["listen_on_ipv6_address"] = session->isIPv6Enabled(); // Save resume data interval data["save_resume_data_interval"] = static_cast(session->saveResumeDataInterval()); // Recheck completed torrents @@ -654,9 +652,6 @@ void AppController::setPreferencesAction() const QHostAddress ifaceAddress {it.value().toString().trimmed()}; session->setNetworkInterfaceAddress(ifaceAddress.isNull() ? QString {} : ifaceAddress.toString()); } - // Listen on IPv6 address - if (hasKey("listen_on_ipv6_address")) - session->setIPv6Enabled(it.value().toBool()); // Save resume data interval if (hasKey("save_resume_data_interval")) session->setSaveResumeDataInterval(it.value().toInt()); @@ -767,14 +762,22 @@ void AppController::networkInterfaceAddressListAction() const QString ifaceName = params().value("iface"); QJsonArray addressList; + const auto appendAddress = [&addressList](const QHostAddress &addr) + { + if (addr.protocol() == QAbstractSocket::IPv6Protocol) + addressList.append(Utils::Net::canonicalIPv6Addr(addr).toString()); + else + addressList.append(addr.toString()); + }; + if (ifaceName.isEmpty()) { - for (const QHostAddress &ip : asConst(QNetworkInterface::allAddresses())) - addressList.append(ip.toString()); + for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses())) + appendAddress(addr); } else { const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName); for (const QNetworkAddressEntry &entry : asConst(iface.addressEntries())) - addressList.append(entry.ip().toString()); + appendAddress(entry.ip()); } setResult(addressList); diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 91db1182d..b021b19a9 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1438,6 +1438,8 @@ if (!addresses) return; $('optionalIPAddressToBind').options.add(new Option('QBT_TR(All addresses)QBT_TR[CONTEXT=OptionDialog]', '')); + $('optionalIPAddressToBind').options.add(new Option('QBT_TR(All IPv4 addresses)QBT_TR[CONTEXT=OptionDialog]', '0.0.0.0')); + $('optionalIPAddressToBind').options.add(new Option('QBT_TR(All IPv6 addresses)QBT_TR[CONTEXT=OptionDialog]', '::')); addresses.forEach(function(item, index) { $('optionalIPAddressToBind').options.add(new Option(item, item)); });