Add support for zero configuration IPv6 streaming

This commit is contained in:
Cameron Gutman 2019-07-14 15:28:50 -07:00
parent bcbd1a5b4b
commit 6f9c3fbc38
6 changed files with 122 additions and 24 deletions

View file

@ -216,6 +216,42 @@ void ComputerManager::saveHosts()
settings.endArray(); settings.endArray();
} }
QHostAddress ComputerManager::getBestGlobalAddressV6(QVector<QHostAddress> &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() void ComputerManager::startPolling()
{ {
QWriteLocker lock(&m_Lock); QWriteLocker lock(&m_Lock);
@ -234,8 +270,8 @@ void ComputerManager::startPolling()
qInfo() << "Discovered mDNS host:" << service.hostname(); qInfo() << "Discovered mDNS host:" << service.hostname();
MdnsPendingComputer* pendingComputer = new MdnsPendingComputer(&m_MdnsServer, &m_MdnsCache, service); MdnsPendingComputer* pendingComputer = new MdnsPendingComputer(&m_MdnsServer, &m_MdnsCache, service);
connect(pendingComputer, SIGNAL(resolvedv4(MdnsPendingComputer*,QHostAddress)), connect(pendingComputer, &MdnsPendingComputer::resolvedHost,
this, SLOT(handleMdnsServiceResolved(MdnsPendingComputer*,QHostAddress))); this, &ComputerManager::handleMdnsServiceResolved);
m_PendingResolution.append(pendingComputer); m_PendingResolution.append(pendingComputer);
}); });
} }
@ -277,11 +313,39 @@ void ComputerManager::startPollingComputer(NvComputer* computer)
} }
void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer, void ComputerManager::handleMdnsServiceResolved(MdnsPendingComputer* computer,
const QHostAddress& address) QVector<QHostAddress>& 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); m_PendingResolution.removeOne(computer);
computer->deleteLater(); computer->deleteLater();
@ -508,9 +572,10 @@ class PendingAddTask : public QObject, public QRunnable
Q_OBJECT Q_OBJECT
public: public:
PendingAddTask(ComputerManager* computerManager, QString address, bool mdns) PendingAddTask(ComputerManager* computerManager, QString address, QHostAddress mdnsIpv6Address, bool mdns)
: m_ComputerManager(computerManager), : m_ComputerManager(computerManager),
m_Address(address), m_Address(address),
m_MdnsIpv6Address(mdnsIpv6Address),
m_Mdns(mdns) m_Mdns(mdns)
{ {
connect(this, &PendingAddTask::computerAddCompleted, connect(this, &PendingAddTask::computerAddCompleted,
@ -560,7 +625,7 @@ private:
{ {
NvHTTP http(m_Address, QSslCertificate()); 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 // Perform initial serverinfo fetch over HTTP since we don't know which cert to use
QString serverInfo = fetchServerInfo(http); QString serverInfo = fetchServerInfo(http);
@ -606,6 +671,11 @@ private:
else { else {
qWarning() << "STUN failed to get WAN address:" << err; 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 { else {
newComputer->manualAddress = m_Address; newComputer->manualAddress = m_Address;
@ -657,14 +727,15 @@ private:
ComputerManager* m_ComputerManager; ComputerManager* m_ComputerManager;
QString m_Address; QString m_Address;
QHostAddress m_MdnsIpv6Address;
bool m_Mdns; 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 // Punt to a worker thread to avoid stalling the
// UI while waiting for serverinfo query to complete // 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); QThreadPool::globalInstance()->start(addTask);
} }

View file

@ -13,6 +13,7 @@
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QSettings> #include <QSettings>
#include <QRunnable> #include <QRunnable>
#include <QTimer>
class MdnsPendingComputer : public QObject class MdnsPendingComputer : public QObject
{ {
@ -25,8 +26,8 @@ public:
: m_Hostname(service.hostname()), : m_Hostname(service.hostname()),
m_Resolver(server, m_Hostname, cache) m_Resolver(server, m_Hostname, cache)
{ {
connect(&m_Resolver, SIGNAL(resolved(QHostAddress)), connect(&m_Resolver, &QMdnsEngine::Resolver::resolved,
this, SLOT(handleResolved(QHostAddress))); this, &MdnsPendingComputer::handleResolvedAddress);
} }
QString hostname() QString hostname()
@ -35,20 +36,31 @@ public:
} }
private slots: private slots:
void handleResolved(const QHostAddress& address) void handleResolvedTimeout()
{ {
if (address.protocol() == QAbstractSocket::IPv4Protocol) { Q_ASSERT(!m_Addresses.isEmpty());
m_Resolver.disconnect(); emit resolvedHost(this, m_Addresses);
emit resolvedv4(this, address); }
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: signals:
void resolvedv4(MdnsPendingComputer*, const QHostAddress&); void resolvedHost(MdnsPendingComputer*,QVector<QHostAddress>&);
private: private:
QByteArray m_Hostname; QByteArray m_Hostname;
QMdnsEngine::Resolver m_Resolver; QMdnsEngine::Resolver m_Resolver;
QVector<QHostAddress> m_Addresses;
}; };
class ComputerPollingEntry class ComputerPollingEntry
@ -140,7 +152,7 @@ public:
Q_INVOKABLE void stopPollingAsync(); 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); void pairHost(NvComputer* computer, QString pin);
@ -165,11 +177,13 @@ private slots:
void handleComputerStateChanged(NvComputer* computer); void handleComputerStateChanged(NvComputer* computer);
void handleMdnsServiceResolved(MdnsPendingComputer* computer, const QHostAddress& address); void handleMdnsServiceResolved(MdnsPendingComputer* computer, QVector<QHostAddress>& addresses);
private: private:
void saveHosts(); void saveHosts();
QHostAddress getBestGlobalAddressV6(QVector<QHostAddress>& addresses);
void startPollingComputer(NvComputer* computer); void startPollingComputer(NvComputer* computer);
int m_PollingRef; int m_PollingRef;

View file

@ -39,11 +39,18 @@ void ComputerSeeker::onComputerUpdated(NvComputer *computer)
bool ComputerSeeker::matchComputer(NvComputer *computer) const bool ComputerSeeker::matchComputer(NvComputer *computer) const
{ {
QString value = m_ComputerName.toLower(); QString value = m_ComputerName.toLower();
return computer->name.toLower() == value ||
computer->localAddress.toLower() == value || if (computer->name.toLower() == value || computer->uuid.toLower() == value) {
computer->remoteAddress.toLower() == value || return true;
computer->manualAddress.toLower() == value || }
computer->uuid.toLower() == value;
for (const QString& addr : computer->uniqueAddresses()) {
if (addr.toLower() == value) {
return true;
}
}
return false;
} }
bool ComputerSeeker::isOnline(NvComputer *computer) const bool ComputerSeeker::isOnline(NvComputer *computer) const

View file

@ -10,6 +10,7 @@
#define SER_LOCALADDR "localaddress" #define SER_LOCALADDR "localaddress"
#define SER_REMOTEADDR "remoteaddress" #define SER_REMOTEADDR "remoteaddress"
#define SER_MANUALADDR "manualaddress" #define SER_MANUALADDR "manualaddress"
#define SER_IPV6ADDR "ipv6address"
#define SER_APPLIST "apps" #define SER_APPLIST "apps"
#define SER_SRVCERT "srvcert" #define SER_SRVCERT "srvcert"
@ -24,6 +25,7 @@ NvComputer::NvComputer(QSettings& settings)
this->macAddress = settings.value(SER_MAC).toByteArray(); this->macAddress = settings.value(SER_MAC).toByteArray();
this->localAddress = settings.value(SER_LOCALADDR).toString(); this->localAddress = settings.value(SER_LOCALADDR).toString();
this->remoteAddress = settings.value(SER_REMOTEADDR).toString(); this->remoteAddress = settings.value(SER_REMOTEADDR).toString();
this->ipv6Address = settings.value(SER_IPV6ADDR).toString();
this->manualAddress = settings.value(SER_MANUALADDR).toString(); this->manualAddress = settings.value(SER_MANUALADDR).toString();
this->serverCert = QSslCertificate(settings.value(SER_SRVCERT).toByteArray()); 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_MAC, macAddress);
settings.setValue(SER_LOCALADDR, localAddress); settings.setValue(SER_LOCALADDR, localAddress);
settings.setValue(SER_REMOTEADDR, remoteAddress); settings.setValue(SER_REMOTEADDR, remoteAddress);
settings.setValue(SER_IPV6ADDR, ipv6Address);
settings.setValue(SER_MANUALADDR, manualAddress); settings.setValue(SER_MANUALADDR, manualAddress);
settings.setValue(SER_SRVCERT, serverCert.toPem()); settings.setValue(SER_SRVCERT, serverCert.toPem());
@ -237,6 +240,7 @@ QVector<QString> NvComputer::uniqueAddresses()
uniqueAddressList.append(activeAddress); uniqueAddressList.append(activeAddress);
uniqueAddressList.append(localAddress); uniqueAddressList.append(localAddress);
uniqueAddressList.append(remoteAddress); uniqueAddressList.append(remoteAddress);
uniqueAddressList.append(ipv6Address);
uniqueAddressList.append(manualAddress); uniqueAddressList.append(manualAddress);
// Prune duplicates (always giving precedence to the first) // 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(macAddress);
ASSIGN_IF_CHANGED_AND_NONEMPTY(localAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(localAddress);
ASSIGN_IF_CHANGED_AND_NONEMPTY(remoteAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(remoteAddress);
ASSIGN_IF_CHANGED_AND_NONEMPTY(ipv6Address);
ASSIGN_IF_CHANGED_AND_NONEMPTY(manualAddress); ASSIGN_IF_CHANGED_AND_NONEMPTY(manualAddress);
ASSIGN_IF_CHANGED(pairState); ASSIGN_IF_CHANGED(pairState);
ASSIGN_IF_CHANGED(serverCodecModeSupport); ASSIGN_IF_CHANGED(serverCodecModeSupport);

View file

@ -64,6 +64,7 @@ public:
// Persisted traits // Persisted traits
QString localAddress; QString localAddress;
QString remoteAddress; QString remoteAddress;
QString ipv6Address;
QString manualAddress; QString manualAddress;
QByteArray macAddress; QByteArray macAddress;
QString name; QString name;

@ -1 +1 @@
Subproject commit 40646208d0599044ce9e47fd3d212e5f942d9303 Subproject commit 4cd72bc39a9a02fb447c2876f216a3e266681bdc