diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e5badc32eb..b125394103 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -231,20 +231,13 @@ qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* a } qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr, - const QUuid& connectionSecret) { - QByteArray datagramCopy = datagram; - - if (!connectionSecret.isNull()) { - // setup the MD5 hash for source verification in the header - replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret); - } - + const QUuid& connectionSecret) { // XXX can BandwidthRecorder be used for this? // stat collection for packets ++_numCollectedPackets; _numCollectedBytes += datagram.size(); - qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, + qint64 bytesWritten = _nodeSocket.writeDatagram(datagram, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); if (bytesWritten < 0) { @@ -270,6 +263,19 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, } } + QByteArray datagramCopy = datagram; + PacketType packetType = packetTypeForPacket(datagramCopy); + + // perform replacement of hash and optionally also sequence number in the header + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType); + replaceHashAndSequenceNumberInPacketGivenType(datagramCopy, packetType, + destinationNode->getConnectionSecret(), + sequenceNumber); + } else { + replaceHashInPacketGivenType(datagramCopy, packetType, destinationNode->getConnectionSecret()); + } + emit dataSent(destinationNode->getType(), datagram.size()); auto bytesWritten = writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret()); // Keep track of per-destination-node bandwidth @@ -318,6 +324,15 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, c 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 diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 6ebe30c930..93be4bcfc2 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -14,7 +14,9 @@ #include #include +#include #include +#include #ifndef _WIN32 #include // not on windows, not needed for mac or windows @@ -34,6 +36,7 @@ #include "DomainHandler.h" #include "Node.h" +#include "PacketHeaders.h" #include "UUIDHasher.h" const int MAX_PACKET_SIZE = 1450; @@ -76,6 +79,8 @@ namespace PingType { const PingType_t Symmetric = 3; } +typedef std::map PacketTypeSequenceMap; + class LimitedNodeList : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -224,8 +229,11 @@ protected: LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton - qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr, + qint64 writeDatagram(const QByteArray& datagram, + const HifiSockAddr& destinationSockAddr, const QUuid& connectionSecret); + + PacketSequenceNumber getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType); void changeSocketBufferSizes(int numBytes); @@ -247,6 +255,8 @@ protected: QElapsedTimer _packetStatTimer; bool _thisNodeCanAdjustLocks; bool _thisNodeCanRez; + + std::unordered_map _packetSequenceNumbers; template void eachNodeHashIterator(IteratorLambda functor) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index a9ccec34bb..f7c43fffb9 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -45,8 +45,8 @@ int packArithmeticallyCodedValue(int value, char* destination) { } } -PacketVersion versionForPacketType(PacketType type) { - switch (type) { +PacketVersion versionForPacketType(PacketType packetType) { + switch (packetType) { case PacketTypeMicrophoneAudioNoEcho: case PacketTypeMicrophoneAudioWithEcho: return 2; @@ -86,8 +86,8 @@ PacketVersion versionForPacketType(PacketType type) { #define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x); -QString nameForPacketType(PacketType type) { - switch (type) { +QString nameForPacketType(PacketType packetType) { + switch (packetType) { PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown); PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse); PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList); @@ -132,30 +132,30 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing); PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply); default: - return QString("Type: ") + QString::number((int)type); + return QString("Type: ") + QString::number((int)packetType); } return QString("unexpected"); } -QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID) { +QByteArray byteArrayWithPopulatedHeader(PacketType packetType, const QUuid& connectionUUID) { QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0); - freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID)); + freshByteArray.resize(populatePacketHeader(freshByteArray, packetType, connectionUUID)); return freshByteArray; } -int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) { - if (packet.size() < numBytesForPacketHeaderGivenPacketType(type)) { - packet.resize(numBytesForPacketHeaderGivenPacketType(type)); +int populatePacketHeader(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID) { + if (packet.size() < numBytesForPacketHeaderGivenPacketType(packetType)) { + packet.resize(numBytesForPacketHeaderGivenPacketType(packetType)); } - return populatePacketHeader(packet.data(), type, connectionUUID); + return populatePacketHeader(packet.data(), packetType, connectionUUID); } -int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) { - int numTypeBytes = packArithmeticallyCodedValue(type, packet); - packet[numTypeBytes] = versionForPacketType(type); +int populatePacketHeader(char* packet, PacketType packetType, const QUuid& connectionUUID) { + int numTypeBytes = packArithmeticallyCodedValue(packetType, packet); + packet[numTypeBytes] = versionForPacketType(packetType); char* position = packet + numTypeBytes + sizeof(PacketVersion); @@ -165,38 +165,46 @@ int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionU memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID); position += NUM_BYTES_RFC4122_UUID; - if (!NON_VERIFIED_PACKETS.contains(type)) { + if (!NON_VERIFIED_PACKETS.contains(packetType)) { // pack 16 bytes of zeros where the md5 hash will be placed once data is packed memset(position, 0, NUM_BYTES_MD5_HASH); position += NUM_BYTES_MD5_HASH; } - + + if (!SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + // Pack zeros for the number of bytes that the sequence number requires. + // The LimitedNodeList will handle packing in the sequence number when sending out the packet. + memset(position, 0, sizeof(PacketSequenceNumber)); + position += sizeof(PacketSequenceNumber); + } + // return the number of bytes written for pointer pushing return position - packet; } int numBytesForPacketHeader(const QByteArray& packet) { - // returns the number of bytes used for the type, version, and UUID - return numBytesArithmeticCodingFromBuffer(packet.data()) - + numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet)) - + NUM_STATIC_HEADER_BYTES; + PacketType packetType = packetTypeForPacket(packet); + return numBytesForPacketHeaderGivenPacketType(packetType); } int numBytesForPacketHeader(const char* packet) { - // returns the number of bytes used for the type, version, and UUID - return numBytesArithmeticCodingFromBuffer(packet) - + numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet)) + PacketType packetType = packetTypeForPacket(packet); + return numBytesForPacketHeaderGivenPacketType(packetType); +} + +int numBytesForPacketHeaderGivenPacketType(PacketType packetType) { + return (int) ceilf((float) packetType / 255) + + numHashBytesForType(packetType) + + numSequenceNumberBytesForType(packetType) + NUM_STATIC_HEADER_BYTES; } -int numBytesForPacketHeaderGivenPacketType(PacketType type) { - return (int) ceilf((float)type / 255) - + numHashBytesInPacketHeaderGivenPacketType(type) - + NUM_STATIC_HEADER_BYTES; +int numHashBytesForType(PacketType packetType) { + return (NON_VERIFIED_PACKETS.contains(packetType) ? 0 : NUM_BYTES_MD5_HASH); } -int numHashBytesInPacketHeaderGivenPacketType(PacketType type) { - return (NON_VERIFIED_PACKETS.contains(type) ? 0 : NUM_BYTES_MD5_HASH); +int numSequenceNumberBytesForType(PacketType packetType) { + return (SEQUENCE_NUMBERED_PACKETS.contains(packetType) ? sizeof(PacketSequenceNumber) : 0); } QUuid uuidFromPacketHeader(const QByteArray& packet) { @@ -204,8 +212,18 @@ QUuid uuidFromPacketHeader(const QByteArray& packet) { NUM_BYTES_RFC4122_UUID)); } +int hashOffsetForPacketType(PacketType packetType) { + return numBytesForPacketHeaderGivenPacketType(packetType) + - (SEQUENCE_NUMBERED_PACKETS.contains(packetType) ? sizeof(PacketSequenceNumber) : 0) + - NUM_BYTES_RFC4122_UUID; +} + +int sequenceNumberOffsetForPacketType(PacketType packetType) { + return numBytesForPacketHeaderGivenPacketType(packetType) - sizeof(PacketSequenceNumber); +} + QByteArray hashFromPacketHeader(const QByteArray& packet) { - return packet.mid(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH); + return packet.mid(hashOffsetForPacketType(packetTypeForPacket(packet)), NUM_BYTES_MD5_HASH); } QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID) { @@ -213,9 +231,25 @@ QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& QCryptographicHash::Md5); } -void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID) { - packet.replace(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH, - hashForPacketAndConnectionUUID(packet, connectionUUID)); +void replaceHashInPacketGivenType(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID) { + packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH, + hashForPacketAndConnectionUUID(packet, connectionUUID)); +} + +void replaceSequenceNumberInPacketGivenType(QByteArray& packet, PacketType packetType, PacketSequenceNumber sequenceNumber) { + packet.replace(sequenceNumberOffsetForPacketType(packetType), + sizeof(PacketTypeSequenceMap), reinterpret_cast(&sequenceNumber)); +} + +void replaceHashAndSequenceNumberInPacketGivenType(QByteArray& packet, PacketType packetType, + const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber) { + replaceHashInPacketGivenType(packet, packetType, connectionUUID); + replaceSequenceNumberInPacketGivenType(packet, packetType, sequenceNumber); +} + + +void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber) { + replaceHashAndSequenceNumberInPacketGivenType(packet, packetTypeForPacket(packet), connectionUUID, sequenceNumber); } PacketType packetTypeForPacket(const QByteArray& packet) { diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 7b12b1a089..dc80feed3c 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -12,14 +12,16 @@ #ifndef hifi_PacketHeaders_h #define hifi_PacketHeaders_h +#include + #include #include #include #include "UUID.h" -// NOTE: if adding a new packet type, you can replace one marked usable or add at the end -// NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well +// NOTE: if adding a new packet packetType, you can replace one marked usable or add at the end +// NOTE: if you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well enum PacketType { PacketTypeUnknown, // 0 PacketTypeStunResponse, @@ -78,6 +80,7 @@ enum PacketType { }; typedef char PacketVersion; +typedef uint16_t PacketSequenceNumber; const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest @@ -88,33 +91,51 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode; +const QSet SEQUENCE_NUMBERED_PACKETS = QSet() + << PacketTypeAvatarData; + const int NUM_BYTES_MD5_HASH = 16; const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_BYTES_MD5_HASH + NUM_STATIC_HEADER_BYTES; -PacketVersion versionForPacketType(PacketType type); -QString nameForPacketType(PacketType type); +PacketType packetTypeForPacket(const QByteArray& packet); +PacketType packetTypeForPacket(const char* packet); + +PacketVersion versionForPacketType(PacketType packetType); +QString nameForPacketType(PacketType packetType); const QUuid nullUUID = QUuid(); -QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID = nullUUID); -int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID = nullUUID); -int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID = nullUUID); +QByteArray byteArrayWithPopulatedHeader(PacketType packetType, const QUuid& connectionUUID = nullUUID); +int populatePacketHeader(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID = nullUUID); +int populatePacketHeader(char* packet, PacketType packetType, const QUuid& connectionUUID = nullUUID); -int numHashBytesInPacketHeaderGivenPacketType(PacketType type); +int numHashBytesForType(PacketType packetType); +int numSequenceNumberBytesForType(PacketType packetType); int numBytesForPacketHeader(const QByteArray& packet); int numBytesForPacketHeader(const char* packet); -int numBytesForPacketHeaderGivenPacketType(PacketType type); +int numBytesForPacketHeaderGivenPacketType(PacketType packetType); QUuid uuidFromPacketHeader(const QByteArray& packet); +int hashOffsetForPacketType(PacketType packetType); +int sequenceNumberOffsetForPacketType(PacketType packetType); + QByteArray hashFromPacketHeader(const QByteArray& packet); QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID); -void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID); -PacketType packetTypeForPacket(const QByteArray& packet); -PacketType packetTypeForPacket(const char* packet); +void replaceHashInPacketGivenType(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID); +void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID) + { replaceHashInPacketGivenType(packet, packetTypeForPacket(packet), connectionUUID); } + +void replaceSequenceNumberInPacketGivenType(QByteArray& packet, PacketType packetType, PacketSequenceNumber sequenceNumber); +void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber) + { replaceSequenceNumberInPacketGivenType(packet, packetTypeForPacket(packet), sequenceNumber); } + +void replaceHashAndSequenceNumberInPacketGivenType(QByteArray& packet, PacketType packetType, + const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber); +void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber); int arithmeticCodingValueFromBuffer(const char* checkValue); int numBytesArithmeticCodingFromBuffer(const char* checkValue);