//// 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. Load more
// // 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));