909 lines · 34.7 KB · C++
RawDownload
////  LimitedNodeList.cpp//  libraries/networking/src////  Created by Stephen Birarda on 2/15/13.//  Copyright 2013 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "LimitedNodeList.h" #include <cstring>#include <cstdlib>#include <cstdio> #include <QtCore/QDataStream>#include <QtCore/QDebug>#include <QtCore/QJsonDocument>#include <QtCore/QUrl>#include <QtNetwork/QHostInfo> #include <tbb/parallel_for.h> #include <LogHandler.h>#include <NumericalConstants.h>#include <SharedUtil.h> #include "AccountManager.h"#include "Assignment.h"#include "HifiSockAddr.h"#include "UUID.h"#include "NetworkLogging.h" const char SOLO_NODE_TYPES[2] = {    NodeType::AvatarMixer,    NodeType::AudioMixer}; LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :    linkedDataCreateCallback(NULL),    _sessionUUID(),    _nodeHash(),    _nodeMutex(QReadWriteLock::Recursive),    _nodeSocket(this),    _dtlsSocket(NULL),    _localSockAddr(),    _publicSockAddr(),    _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),    _packetStatTimer(),    _thisNodeCanAdjustLocks(false),    _thisNodeCanRez(true){    static bool firstCall = true;    if (firstCall) {        NodeType::init();         // register the SharedNodePointer meta-type for signals/slots        qRegisterMetaType<SharedNodePointer>();        firstCall = false;    }     qRegisterMetaType<ConnectionStep>("ConnectionStep");     _nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);    qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();     if (dtlsListenPort > 0) {        // only create the DTLS socket during constructor if a custom port is passed        _dtlsSocket = new QUdpSocket(this);         _dtlsSocket->bind(QHostAddress::AnyIPv4, dtlsListenPort);        qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();    }     const int LARGER_BUFFER_SIZE = 1048576;    changeSocketBufferSizes(LARGER_BUFFER_SIZE);     // check for local socket updates every so often    const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;    QTimer* localSocketUpdate = new QTimer(this);    connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr);    localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS);     QTimer* silentNodeTimer = new QTimer(this);    connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes);    silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);     // check the local socket right now    updateLocalSockAddr();     _packetStatTimer.start();} void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {    QUuid oldUUID = _sessionUUID;    _sessionUUID = sessionUUID;     if (sessionUUID != oldUUID) {        qCDebug(networking) << "NodeList UUID changed from" <<  uuidStringWithoutCurlyBraces(oldUUID)        << "to" << uuidStringWithoutCurlyBraces(_sessionUUID);        emit uuidChanged(sessionUUID, oldUUID);    }} void LimitedNodeList::setThisNodeCanAdjustLocks(bool canAdjustLocks) {    if (_thisNodeCanAdjustLocks != canAdjustLocks) {        _thisNodeCanAdjustLocks = canAdjustLocks;        emit canAdjustLocksChanged(canAdjustLocks);    }} void LimitedNodeList::setThisNodeCanRez(bool canRez) {    if (_thisNodeCanRez != canRez) {        _thisNodeCanRez = canRez;        emit canRezChanged(canRez);    }} QUdpSocket& LimitedNodeList::getDTLSSocket() {    if (!_dtlsSocket) {        // DTLS socket getter called but no DTLS socket exists, create it now        _dtlsSocket = new QUdpSocket(this);         _dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress);         // we're using DTLS and our socket is good to go, so make the required DTLS changes        // DTLS requires that IP_DONTFRAG be set        // This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it         qCDebug(networking) << "LimitedNodeList DTLS socket is listening on" << _dtlsSocket->localPort();    }     return *_dtlsSocket;} void LimitedNodeList::changeSocketBufferSizes(int numBytes) {    for (int i = 0; i < 2; i++) {        QAbstractSocket::SocketOption bufferOpt;        QString bufferTypeString;        if (i == 0) {            bufferOpt = QAbstractSocket::SendBufferSizeSocketOption;            bufferTypeString = "send";         } else {            bufferOpt = QAbstractSocket::ReceiveBufferSizeSocketOption;            bufferTypeString = "receive";        }        int oldBufferSize = _nodeSocket.socketOption(bufferOpt).toInt();        if (oldBufferSize < numBytes) {            int newBufferSize = _nodeSocket.socketOption(bufferOpt).toInt();             qCDebug(networking) << "Changed socket" << bufferTypeString << "buffer size from" << oldBufferSize << "to"                << newBufferSize << "bytes";        } else {            // don't make the buffer smaller            qCDebug(networking) << "Did not change socket" << bufferTypeString << "buffer size from" << oldBufferSize                << "since it is larger than desired size of" << numBytes;        }    }} bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {    PacketType checkType = packetTypeForPacket(packet);    int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());     if (packet[numPacketTypeBytes] != versionForPacketType(checkType)        && checkType != PacketTypeStunResponse) {        PacketType mismatchType = packetTypeForPacket(packet);         static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;         QUuid senderUUID = uuidFromPacketHeader(packet);        if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {            qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"            << uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"            << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";             emit packetVersionMismatch();             versionDebugSuppressMap.insert(senderUUID, checkType);        }         return false;    }     if (!NON_VERIFIED_PACKETS.contains(checkType)) {        // figure out which node this is from        SharedNodePointer sendingNode = sendingNodeForPacket(packet);        if (sendingNode) {            // check if the md5 hash in the header matches the hash we would expect            if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {                return true;            } else {                static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;                 QUuid senderUUID = uuidFromPacketHeader(packet);                if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {                    qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender"                    << uuidFromPacketHeader(packet);                     hashDebugSuppressMap.insert(senderUUID, checkType);                }            }        } else {            static QString repeatedMessage                = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID");             qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID"                << qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet)));        }    } else {        return true;    }     return false;} // qint64 LimitedNodeList::readDatagram(char* data, qint64 maxSize, QHostAddress* address = 0, quint16 * port = 0) { qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* address = 0, quint16 * port = 0) {    qint64 result = getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), address, port);     SharedNodePointer sendingNode = sendingNodeForPacket(incomingPacket);    if (sendingNode) {        emit dataReceived(sendingNode->getType(), incomingPacket.size());    } else {        emit dataReceived(NodeType::Unassigned, incomingPacket.size());    }     return result;} qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) {    // XXX can BandwidthRecorder be used for this?    // stat collection for packets    ++_numCollectedPackets;    _numCollectedBytes += datagram.size();     qint64 bytesWritten = _nodeSocket.writeDatagram(datagram,                                                    destinationSockAddr.getAddress(), destinationSockAddr.getPort());     if (bytesWritten < 0) {        qCDebug(networking) << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString();    }     return bytesWritten;} qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,                                      const SharedNodePointer& destinationNode,                                      const HifiSockAddr& overridenSockAddr) {    if (destinationNode) {        PacketType packetType = packetTypeForPacket(datagram);         if (NON_VERIFIED_PACKETS.contains(packetType)) {            return writeUnverifiedDatagram(datagram, destinationNode, overridenSockAddr);        }         // if we don't have an overridden address, assume they want to send to the node's active socket        const HifiSockAddr* destinationSockAddr = &overridenSockAddr;        if (overridenSockAddr.isNull()) {            if (destinationNode->getActiveSocket()) {                // use the node's active socket as the destination socket                destinationSockAddr = destinationNode->getActiveSocket();            } else {                // we don't have a socket to send to, return 0                return 0;            }        }         QByteArray datagramCopy = datagram;         // if we're here and the connection secret is null, debug out - this could be a problem        if (destinationNode->getConnectionSecret().isNull()) {            qDebug() << "LimitedNodeList::writeDatagram called for verified datagram with null connection secret for"                << "destination node" << destinationNode->getUUID() << " - this is either not secure or will cause"                << "this packet to be unverifiable on the receiving side.";        }         // perform replacement of hash and optionally also sequence number in the header        if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {            PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);            replaceHashAndSequenceNumberInPacket(datagramCopy, destinationNode->getConnectionSecret(),                                                 sequenceNumber, packetType);        } else {            replaceHashInPacket(datagramCopy, destinationNode->getConnectionSecret(), packetType);        }         emit dataSent(destinationNode->getType(), datagram.size());        auto bytesWritten = writeDatagram(datagramCopy, *destinationSockAddr);        // Keep track of per-destination-node bandwidth        destinationNode->recordBytesSent(bytesWritten);        return bytesWritten;    }     // didn't have a destinationNode to send to, return 0    return 0;} qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,                               const HifiSockAddr& overridenSockAddr) {    if (destinationNode) {        // if we don't have an ovveriden address, assume they want to send to the node's active socket        const HifiSockAddr* destinationSockAddr = &overridenSockAddr;        if (overridenSockAddr.isNull()) {            if (destinationNode->getActiveSocket()) {                // use the node's active socket as the destination socket                destinationSockAddr = destinationNode->getActiveSocket();            } else {                // we don't have a socket to send to, return 0                return 0;            }        }         PacketType packetType = packetTypeForPacket(datagram);         // optionally peform sequence number replacement in the header        if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {             QByteArray datagramCopy = datagram;             PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);            replaceSequenceNumberInPacket(datagramCopy, sequenceNumber, packetType);             // send the datagram with sequence number replaced in header            return writeDatagram(datagramCopy, *destinationSockAddr);        } else {            return writeDatagram(datagram, *destinationSockAddr);        }    }     // didn't have a destinationNode to send to, return 0    return 0;} qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) {    return writeDatagram(datagram, destinationSockAddr);} qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,                               const HifiSockAddr& overridenSockAddr) {    return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);} qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,                               const HifiSockAddr& overridenSockAddr) {    return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);} PacketSequenceNumber LimitedNodeList::getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType) {    // Thanks to std::map and std::unordered_map this line either default constructs the    // PacketTypeSequenceMap and the PacketSequenceNumber or returns the existing value.    // We use the postfix increment so that the stored value is incremented and the next    // return gives the correct value.     return _packetSequenceNumbers[nodeUUID][packetType]++;} void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {    // the node decided not to do anything with this packet    // if it comes from a known source we should keep that node alive    SharedNodePointer matchingNode = sendingNodeForPacket(packet);    if (matchingNode) {        matchingNode->setLastHeardMicrostamp(usecTimestampNow());    }} int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) {    QMutexLocker locker(&matchingNode->getMutex());     matchingNode->setLastHeardMicrostamp(usecTimestampNow());     // if this was a sequence numbered packet we should store the last seq number for    // a packet of this type for this node    PacketType packetType = packetTypeForPacket(packet);    if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {        matchingNode->setLastSequenceNumberForPacketType(sequenceNumberFromHeader(packet, packetType), packetType);    }     NodeData* linkedData = matchingNode->getLinkedData();    if (!linkedData && linkedDataCreateCallback) {        linkedDataCreateCallback(matchingNode.data());    }     if (linkedData) {        QMutexLocker linkedDataLocker(&linkedData->getMutex());        return linkedData->parseData(packet);    }    return 0;} int LimitedNodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {    SharedNodePointer matchingNode = sendingNodeForPacket(packet);     if (matchingNode) {        return updateNodeWithDataFromPacket(matchingNode, packet);    }     // we weren't able to match the sender address to the address we have for this node, unlock and don't parse    return 0;} SharedNodePointer LimitedNodeList::nodeWithUUID(const QUuid& nodeUUID) {    QReadLocker readLocker(&_nodeMutex);     NodeHash::const_iterator it = _nodeHash.find(nodeUUID);    return it == _nodeHash.cend() ? SharedNodePointer() : it->second; } SharedNodePointer LimitedNodeList::sendingNodeForPacket(const QByteArray& packet) {    QUuid nodeUUID = uuidFromPacketHeader(packet);     // return the matching node, or NULL if there is no match    return nodeWithUUID(nodeUUID);} void LimitedNodeList::eraseAllNodes() {    qCDebug(networking) << "Clearing the NodeList. Deleting all nodes in list.";     QSet<SharedNodePointer> killedNodes;    eachNode([&killedNodes](const SharedNodePointer& node){        killedNodes.insert(node);    });     // iterate the current nodes, emit that they are dying and remove them from the hash    _nodeMutex.lockForWrite();    _nodeHash.clear();    _nodeMutex.unlock();     foreach(const SharedNodePointer& killedNode, killedNodes) {        handleNodeKill(killedNode);    }} void LimitedNodeList::reset() {    eraseAllNodes();} void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {    _nodeMutex.lockForRead();     NodeHash::iterator it = _nodeHash.find(nodeUUID);    if (it != _nodeHash.end()) {        SharedNodePointer matchingNode = it->second;         _nodeMutex.unlock();         _nodeMutex.lockForWrite();        _nodeHash.unsafe_erase(it);        _nodeMutex.unlock();         handleNodeKill(matchingNode);    } else {        _nodeMutex.unlock();    }} void LimitedNodeList::processKillNode(const QByteArray& dataByteArray) {    // read the node id    QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(dataByteArray), NUM_BYTES_RFC4122_UUID));     // kill the node with this UUID, if it exists    killNodeWithUUID(nodeUUID);} void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) {    qCDebug(networking) << "Killed" << *node;    emit nodeKilled(node);} SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,                                                   const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,                                                   bool canAdjustLocks, bool canRez,                                                   const QUuid& connectionSecret) {    NodeHash::const_iterator it = _nodeHash.find(uuid);     if (it != _nodeHash.end()) {        SharedNodePointer& matchingNode = it->second;         matchingNode->setPublicSocket(publicSocket);        matchingNode->setLocalSocket(localSocket);        matchingNode->setCanAdjustLocks(canAdjustLocks);        matchingNode->setCanRez(canRez);        matchingNode->setConnectionSecret(connectionSecret);         return matchingNode;    } else {        // we didn't have this node, so add them        Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret, this);         if (nodeType == NodeType::AudioMixer) {            LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer);        }         SharedNodePointer newNodePointer(newNode);         _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); 

Lines 1–500 of 909.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t