226d06402b
The system was just treating IP (and optionally port) as a unique identifier, so if a peer had multiple possible paths to a client they would connect multiple times. This fixes that by generating using QUuid in each client. We then use this during broadcast, replacing the username we sent before (which was not used), and as part of the greeting. The greeting now is more complex, since we need to send both username and the ID. Change-Id: I6c6c2ffd5198406aad48445a68dd6aab36de69c0 Reviewed-by: Konrad Kujawa <konrad.kujawa@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
156 lines
4.6 KiB
C++
156 lines
4.6 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// Copyright (C) 2018 Intel Corporation.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
|
|
#include "client.h"
|
|
#include "connection.h"
|
|
#include "peermanager.h"
|
|
|
|
#include <QNetworkInterface>
|
|
#include <QUuid>
|
|
|
|
static const qint32 BroadcastInterval = 2000;
|
|
static const unsigned broadcastPort = 45000;
|
|
|
|
PeerManager::PeerManager(Client *client)
|
|
: QObject(client), client(client)
|
|
{
|
|
static const char *envVariables[] = {
|
|
"USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME"
|
|
};
|
|
|
|
for (const char *varname : envVariables) {
|
|
username = qEnvironmentVariable(varname);
|
|
if (!username.isNull())
|
|
break;
|
|
}
|
|
|
|
if (username.isEmpty())
|
|
username = "unknown";
|
|
|
|
// We generate a unique per-process identifier so we can avoid multiple
|
|
// connections to/from the same remote peer as well as ignore our own
|
|
// broadcasts.
|
|
localUniqueId = QUuid::createUuid().toByteArray();
|
|
|
|
updateAddresses();
|
|
|
|
broadcastSocket.bind(QHostAddress::Any, broadcastPort, QUdpSocket::ShareAddress
|
|
| QUdpSocket::ReuseAddressHint);
|
|
connect(&broadcastSocket, &QUdpSocket::readyRead,
|
|
this, &PeerManager::readBroadcastDatagram);
|
|
|
|
broadcastTimer.setInterval(BroadcastInterval);
|
|
connect(&broadcastTimer, &QTimer::timeout,
|
|
this, &PeerManager::sendBroadcastDatagram);
|
|
}
|
|
|
|
void PeerManager::setServerPort(int port)
|
|
{
|
|
serverPort = port;
|
|
}
|
|
|
|
QString PeerManager::userName() const
|
|
{
|
|
return username;
|
|
}
|
|
|
|
QByteArray PeerManager::uniqueId() const
|
|
{
|
|
return localUniqueId;
|
|
}
|
|
|
|
void PeerManager::startBroadcasting()
|
|
{
|
|
broadcastTimer.start();
|
|
}
|
|
|
|
bool PeerManager::isLocalHostAddress(const QHostAddress &address) const
|
|
{
|
|
return ipAddresses.contains(address);
|
|
}
|
|
|
|
void PeerManager::sendBroadcastDatagram()
|
|
{
|
|
QByteArray datagram;
|
|
{
|
|
QCborStreamWriter writer(&datagram);
|
|
writer.startArray(2);
|
|
writer.append(localUniqueId);
|
|
writer.append(serverPort);
|
|
writer.endArray();
|
|
}
|
|
|
|
bool validBroadcastAddresses = true;
|
|
for (const QHostAddress &address : std::as_const(broadcastAddresses)) {
|
|
if (broadcastSocket.writeDatagram(datagram, address, broadcastPort) == -1)
|
|
validBroadcastAddresses = false;
|
|
}
|
|
|
|
if (!validBroadcastAddresses)
|
|
updateAddresses();
|
|
}
|
|
|
|
void PeerManager::readBroadcastDatagram()
|
|
{
|
|
while (broadcastSocket.hasPendingDatagrams()) {
|
|
QHostAddress senderIp;
|
|
quint16 senderPort;
|
|
QByteArray datagram;
|
|
datagram.resize(broadcastSocket.pendingDatagramSize());
|
|
if (broadcastSocket.readDatagram(datagram.data(), datagram.size(),
|
|
&senderIp, &senderPort) == -1)
|
|
continue;
|
|
|
|
int senderServerPort;
|
|
QByteArray peerUniqueId;
|
|
{
|
|
// decode the datagram
|
|
QCborStreamReader reader(datagram);
|
|
if (reader.lastError() != QCborError::NoError || !reader.isArray())
|
|
continue;
|
|
if (!reader.isLengthKnown() || reader.length() != 2)
|
|
continue;
|
|
|
|
reader.enterContainer();
|
|
if (reader.lastError() != QCborError::NoError || !reader.isByteArray())
|
|
continue;
|
|
auto r = reader.readByteArray();
|
|
while (r.status == QCborStreamReader::Ok) {
|
|
peerUniqueId = r.data;
|
|
r = reader.readByteArray();
|
|
}
|
|
|
|
if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger())
|
|
continue;
|
|
senderServerPort = reader.toInteger();
|
|
}
|
|
|
|
if (peerUniqueId == localUniqueId)
|
|
continue;
|
|
|
|
if (!client->hasConnection(peerUniqueId)) {
|
|
Connection *connection = new Connection(this);
|
|
emit newConnection(connection);
|
|
connection->connectToHost(senderIp, senderServerPort);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PeerManager::updateAddresses()
|
|
{
|
|
broadcastAddresses.clear();
|
|
ipAddresses.clear();
|
|
const QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
|
for (const QNetworkInterface &interface : interfaces) {
|
|
const QList<QNetworkAddressEntry> entries = interface.addressEntries();
|
|
for (const QNetworkAddressEntry &entry : entries) {
|
|
QHostAddress broadcastAddress = entry.broadcast();
|
|
if (broadcastAddress != QHostAddress::Null && entry.ip() != QHostAddress::LocalHost) {
|
|
broadcastAddresses << broadcastAddress;
|
|
ipAddresses << entry.ip();
|
|
}
|
|
}
|
|
}
|
|
}
|