diff --git a/app/backend/computermanager.cpp b/app/backend/computermanager.cpp index 1556bff4..edccea6e 100644 --- a/app/backend/computermanager.cpp +++ b/app/backend/computermanager.cpp @@ -216,6 +216,42 @@ void ComputerManager::saveHosts() settings.endArray(); } +QHostAddress ComputerManager::getBestGlobalAddressV6(QVector &addresses) +{ + for (const QHostAddress& address : addresses) { + if (address.protocol() == QAbstractSocket::IPv6Protocol) { + if (address.isInSubnet(QHostAddress("fe80::"), 10)) { + // Link-local + continue; + } + + if (address.isInSubnet(QHostAddress("fec0::"), 10)) { + qInfo() << "Ignoring site-local address:" << address; + continue; + } + + if (address.isInSubnet(QHostAddress("fc00::"), 7)) { + qInfo() << "Ignoring ULA:" << address; + continue; + } + + if (address.isInSubnet(QHostAddress("2002::"), 16)) { + qInfo() << "Ignoring 6to4 address:" << address; + continue; + } + + if (address.isInSubnet(QHostAddress("2001::"), 32)) { + qInfo() << "Ignoring Teredo address:" << address; + continue; + } + + return address; + } + } + + return QHostAddress(); +} + void ComputerManager::startPolling() { QWriteLocker lock(&m_Lock); @@ -234,8 +270,8 @@ void ComputerManager::startPolling() qInfo() << "Discovered mDNS host:" << service.hostname(); MdnsPendingComputer* pendingComputer = new MdnsPendingComputer(&m_MdnsServer, &m_MdnsCache, service); - connect(pendingComputer, SIGNAL(resolvedv4(MdnsPendingComputer*,QHostAddress)), - this, SLOT(handleMdnsServiceResolved(MdnsPendingComputer*,QHostAddress))); + connect(pendingComputer, &MdnsPendingComputer::resolvedHost, + this, &ComputerManager::handleMdnsServiceResolved); m_PendingResolution.append(pendingComputer); }); } @@ -277,11 +313,39 @@ void ComputerManager::startPollingComputer(NvComputer* computer) } void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer, - const QHostAddress& address) + QVector& addresses) { - qInfo() << "Resolved" << computer->hostname() << "to" << address.toString(); + QHostAddress v6Global = getBestGlobalAddressV6(addresses); + bool added = false; - addNewHost(address.toString(), true); + // Add the host using the IPv4 address + for (const QHostAddress& address : addresses) { + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + // NB: We don't just call addNewHost() here with v6Global because the IPv6 + // address may not be reachable (if the user hasn't installed the IPv6 helper yet + // or if this host lacks outbound IPv6 capability). We want to add IPv6 even if + // it's not currently reachable. + addNewHost(address.toString(), true, v6Global); + added = true; + break; + } + } + + if (!added) { + // If we get here, there wasn't an IPv4 address so we'll do it v6-only + for (const QHostAddress& address : addresses) { + if (address.protocol() == QAbstractSocket::IPv6Protocol) { + // Use a link-local or site-local address for the "local address" + if (address.isInSubnet(QHostAddress("fe80::"), 10) || + address.isInSubnet(QHostAddress("fec0::"), 10) || + address.isInSubnet(QHostAddress("fc00::"), 7)) { + addNewHost(address.toString(), true, v6Global); + added = true; + break; + } + } + } + } m_PendingResolution.removeOne(computer); computer->deleteLater(); @@ -508,9 +572,10 @@ class PendingAddTask : public QObject, public QRunnable Q_OBJECT public: - PendingAddTask(ComputerManager* computerManager, QString address, bool mdns) + PendingAddTask(ComputerManager* computerManager, QString address, QHostAddress mdnsIpv6Address, bool mdns) : m_ComputerManager(computerManager), m_Address(address), + m_MdnsIpv6Address(mdnsIpv6Address), m_Mdns(mdns) { connect(this, &PendingAddTask::computerAddCompleted, @@ -560,7 +625,7 @@ private: { NvHTTP http(m_Address, QSslCertificate()); - qInfo() << "Processing new PC at" << m_Address << "from" << (m_Mdns ? "mDNS" : "user"); + qInfo() << "Processing new PC at" << m_Address << "from" << (m_Mdns ? "mDNS" : "user") << m_MdnsIpv6Address; // Perform initial serverinfo fetch over HTTP since we don't know which cert to use QString serverInfo = fetchServerInfo(http); @@ -606,6 +671,11 @@ private: else { qWarning() << "STUN failed to get WAN address:" << err; } + + if (!m_MdnsIpv6Address.isNull()) { + Q_ASSERT(m_MdnsIpv6Address.protocol() == QAbstractSocket::IPv6Protocol); + newComputer->ipv6Address = m_MdnsIpv6Address.toString(); + } } else { newComputer->manualAddress = m_Address; @@ -657,14 +727,15 @@ private: ComputerManager* m_ComputerManager; QString m_Address; + QHostAddress m_MdnsIpv6Address; bool m_Mdns; }; -void ComputerManager::addNewHost(QString address, bool mdns) +void ComputerManager::addNewHost(QString address, bool mdns, QHostAddress mdnsIpv6Address) { // Punt to a worker thread to avoid stalling the // UI while waiting for serverinfo query to complete - PendingAddTask* addTask = new PendingAddTask(this, address, mdns); + PendingAddTask* addTask = new PendingAddTask(this, address, mdnsIpv6Address, mdns); QThreadPool::globalInstance()->start(addTask); } diff --git a/app/backend/computermanager.h b/app/backend/computermanager.h index 6d21c909..f4d3dc63 100644 --- a/app/backend/computermanager.h +++ b/app/backend/computermanager.h @@ -13,6 +13,7 @@ #include #include #include +#include class MdnsPendingComputer : public QObject { @@ -25,8 +26,8 @@ public: : m_Hostname(service.hostname()), m_Resolver(server, m_Hostname, cache) { - connect(&m_Resolver, SIGNAL(resolved(QHostAddress)), - this, SLOT(handleResolved(QHostAddress))); + connect(&m_Resolver, &QMdnsEngine::Resolver::resolved, + this, &MdnsPendingComputer::handleResolvedAddress); } QString hostname() @@ -35,20 +36,31 @@ public: } private slots: - void handleResolved(const QHostAddress& address) + void handleResolvedTimeout() { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - m_Resolver.disconnect(); - emit resolvedv4(this, address); + Q_ASSERT(!m_Addresses.isEmpty()); + emit resolvedHost(this, m_Addresses); + } + + void handleResolvedAddress(const QHostAddress& address) + { + qInfo() << "Resolved" << hostname() << "to" << address; + m_Addresses.push_back(address); + + // Now that we got an address, start a timer to wait for more + // addresses to come in before reporting them + if (m_Addresses.count() == 1) { + QTimer::singleShot(1000, this, SLOT(handleResolvedTimeout())); } } signals: - void resolvedv4(MdnsPendingComputer*, const QHostAddress&); + void resolvedHost(MdnsPendingComputer*,QVector&); private: QByteArray m_Hostname; QMdnsEngine::Resolver m_Resolver; + QVector m_Addresses; }; class ComputerPollingEntry @@ -140,7 +152,7 @@ public: Q_INVOKABLE void stopPollingAsync(); - Q_INVOKABLE void addNewHost(QString address, bool mdns); + Q_INVOKABLE void addNewHost(QString address, bool mdns, QHostAddress mdnsIpv6Address = QHostAddress()); void pairHost(NvComputer* computer, QString pin); @@ -165,11 +177,13 @@ private slots: void handleComputerStateChanged(NvComputer* computer); - void handleMdnsServiceResolved(MdnsPendingComputer* computer, const QHostAddress& address); + void handleMdnsServiceResolved(MdnsPendingComputer* computer, QVector& addresses); private: void saveHosts(); + QHostAddress getBestGlobalAddressV6(QVector& addresses); + void startPollingComputer(NvComputer* computer); int m_PollingRef; diff --git a/app/backend/computerseeker.cpp b/app/backend/computerseeker.cpp index 58771ff1..c7674a5a 100644 --- a/app/backend/computerseeker.cpp +++ b/app/backend/computerseeker.cpp @@ -39,11 +39,18 @@ void ComputerSeeker::onComputerUpdated(NvComputer *computer) bool ComputerSeeker::matchComputer(NvComputer *computer) const { QString value = m_ComputerName.toLower(); - return computer->name.toLower() == value || - computer->localAddress.toLower() == value || - computer->remoteAddress.toLower() == value || - computer->manualAddress.toLower() == value || - computer->uuid.toLower() == value; + + if (computer->name.toLower() == value || computer->uuid.toLower() == value) { + return true; + } + + for (const QString& addr : computer->uniqueAddresses()) { + if (addr.toLower() == value) { + return true; + } + } + + return false; } bool ComputerSeeker::isOnline(NvComputer *computer) const diff --git a/app/backend/nvcomputer.cpp b/app/backend/nvcomputer.cpp index 815bdbdf..e69bb3be 100644 --- a/app/backend/nvcomputer.cpp +++ b/app/backend/nvcomputer.cpp @@ -10,6 +10,7 @@ #define SER_LOCALADDR "localaddress" #define SER_REMOTEADDR "remoteaddress" #define SER_MANUALADDR "manualaddress" +#define SER_IPV6ADDR "ipv6address" #define SER_APPLIST "apps" #define SER_SRVCERT "srvcert" @@ -24,6 +25,7 @@ NvComputer::NvComputer(QSettings& settings) this->macAddress = settings.value(SER_MAC).toByteArray(); this->localAddress = settings.value(SER_LOCALADDR).toString(); this->remoteAddress = settings.value(SER_REMOTEADDR).toString(); + this->ipv6Address = settings.value(SER_IPV6ADDR).toString(); this->manualAddress = settings.value(SER_MANUALADDR).toString(); this->serverCert = QSslCertificate(settings.value(SER_SRVCERT).toByteArray()); @@ -63,6 +65,7 @@ void NvComputer::serialize(QSettings& settings) settings.setValue(SER_MAC, macAddress); settings.setValue(SER_LOCALADDR, localAddress); settings.setValue(SER_REMOTEADDR, remoteAddress); + settings.setValue(SER_IPV6ADDR, ipv6Address); settings.setValue(SER_MANUALADDR, manualAddress); settings.setValue(SER_SRVCERT, serverCert.toPem()); @@ -237,6 +240,7 @@ QVector NvComputer::uniqueAddresses() uniqueAddressList.append(activeAddress); uniqueAddressList.append(localAddress); uniqueAddressList.append(remoteAddress); + uniqueAddressList.append(ipv6Address); uniqueAddressList.append(manualAddress); // Prune duplicates (always giving precedence to the first) @@ -296,6 +300,7 @@ bool NvComputer::update(NvComputer& that) ASSIGN_IF_CHANGED_AND_NONEMPTY(macAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(localAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(remoteAddress); + ASSIGN_IF_CHANGED_AND_NONEMPTY(ipv6Address); ASSIGN_IF_CHANGED_AND_NONEMPTY(manualAddress); ASSIGN_IF_CHANGED(pairState); ASSIGN_IF_CHANGED(serverCodecModeSupport); diff --git a/app/backend/nvcomputer.h b/app/backend/nvcomputer.h index c848dfd3..4da26477 100644 --- a/app/backend/nvcomputer.h +++ b/app/backend/nvcomputer.h @@ -64,6 +64,7 @@ public: // Persisted traits QString localAddress; QString remoteAddress; + QString ipv6Address; QString manualAddress; QByteArray macAddress; QString name; diff --git a/qmdnsengine/qmdnsengine b/qmdnsengine/qmdnsengine index 40646208..4cd72bc3 160000 --- a/qmdnsengine/qmdnsengine +++ b/qmdnsengine/qmdnsengine @@ -1 +1 @@ -Subproject commit 40646208d0599044ce9e47fd3d212e5f942d9303 +Subproject commit 4cd72bc39a9a02fb447c2876f216a3e266681bdc