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; }
void Packet::writePacketTypeAndVersion(PacketType::Value type) { // Pack the packet type auto offset = packArithmeticallyCodedValue(type, _packet.get()); // Pack the packet version auto version = versionForPacketType(type); memcpy(_packet.get() + offset, &version, sizeof(version)); }
NLPacket::NLPacket(PacketType type, qint64 size, bool isReliable, bool isPartOfMessage) : Packet((size == -1) ? -1 : NLPacket::localHeaderSize(type) + size, isReliable, isPartOfMessage), _type(type), _version(versionForPacketType(type)) { adjustPayloadStartAndCapacity(NLPacket::localHeaderSize(_type)); writeTypeAndVersion(); }
void NLPacket::setType(PacketType type) { // Setting new packet type with a different header size not currently supported Q_ASSERT(NLPacket::totalHeaderSize(_type, isPartOfMessage()) == NLPacket::totalHeaderSize(type, isPartOfMessage())); _type = type; _version = versionForPacketType(_type); writeTypeAndVersion(); }
bool packetVersionMatch(const QByteArray& packet) { // currently this just checks if the version in the packet matches our return from versionForPacketType // may need to be expanded in the future for types and versions that take > than 1 byte if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packetTypeForPacket(packet) == PacketTypeStunResponse) { return true; } else { PacketType mismatchType = packetTypeForPacket(packet); int numPacketTypeBytes = arithmeticCodingValueFromBuffer(packet.data()); QUuid nodeUUID; deconstructPacketHeader(packet, nodeUUID); qDebug() << "Packet mismatch on" << packetTypeForPacket(packet) << "- Sender" << nodeUUID << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but" << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected."; return false; } }
int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) { int numTypeBytes = packArithmeticallyCodedValue(type, packet); packet[numTypeBytes] = versionForPacketType(type); QUuid packUUID = connectionUUID.isNull() ? NodeList::getInstance()->getOwnerUUID() : connectionUUID; QByteArray rfcUUID = packUUID.toRfc4122(); memcpy(packet + numTypeBytes + sizeof(PacketVersion), rfcUUID.constData(), NUM_BYTES_RFC4122_UUID); // return the number of bytes written for pointer pushing return numTypeBytes + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; }
static void ensureProtocolVersionsSignature() { static std::once_flag once; std::call_once(once, [&] { QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); uint8_t numberOfProtocols = static_cast<uint8_t>(PacketType::LAST_PACKET_TYPE) + 1; stream << numberOfProtocols; for (uint8_t packetType = 0; packetType < numberOfProtocols; packetType++) { uint8_t packetTypeVersion = static_cast<uint8_t>(versionForPacketType(static_cast<PacketType>(packetType))); stream << packetTypeVersion; } QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(buffer); protocolVersionSignature = hash.result(); protocolVersionSignatureBase64 = protocolVersionSignature.toBase64(); }); }
int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) { int numTypeBytes = packArithmeticallyCodedValue(type, packet); packet[numTypeBytes] = versionForPacketType(type); char* position = packet + numTypeBytes + sizeof(PacketVersion); QUuid packUUID = connectionUUID.isNull() ? NodeList::getInstance()->getSessionUUID() : connectionUUID; QByteArray rfcUUID = packUUID.toRfc4122(); memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID); position += NUM_BYTES_RFC4122_UUID; // pack 16 bytes of zeros where the md5 hash will be placed one data is packed memset(position, 0, NUM_BYTES_MD5_HASH); position += NUM_BYTES_MD5_HASH; // return the number of bytes written for pointer pushing return position - packet; }
void DataServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (_socket.hasPendingDatagrams()) { receivedPacket.resize(_socket.pendingDatagramSize()); _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); PacketType requestType = packetTypeForPacket(receivedPacket); if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) && receivedPacket[numBytesArithmeticCodingFromBuffer(receivedPacket.data())] == versionForPacketType(requestType)) { QDataStream packetStream(receivedPacket); int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket); packetStream.skipRawData(numReceivedHeaderBytes); // pull the sequence number used for this packet quint8 sequenceNumber = 0; packetStream >> sequenceNumber; // pull the UUID that we will need as part of the key QString userString; packetStream >> userString; QUuid parsedUUID(userString); if (parsedUUID.isNull()) { // we failed to parse a UUID, this means the user has sent us a username // ask redis for the UUID for this user redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString)); if (reply->type == REDIS_REPLY_STRING) { parsedUUID = QUuid(QString(reply->str)); } if (!parsedUUID.isNull()) { qDebug() << "Found UUID" << parsedUUID << "for username" << userString; } else { qDebug() << "Failed UUID lookup for username" << userString; } freeReplyObject(reply); reply = NULL; } if (!parsedUUID.isNull()) { if (requestType == PacketTypeDataServerPut) { // pull the key and value that specifies the data the user is putting/getting QString dataKey, dataValue; packetStream >> dataKey >> dataValue; qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm // which is a reply packet with the sequence number QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid); replyPacket.append(sequenceNumber); _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } freeReplyObject(reply); } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid); QDataStream sendPacketStream(&sendPacket, QIODevice::Append); sendPacketStream << sequenceNumber; // pull the key list that specifies the data the user is putting/getting QString keyListString; packetStream >> keyListString; if (keyListString != "uuid") { // copy the parsed UUID sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); const char MULTI_KEY_VALUE_SEPARATOR = '|'; // append the keyListString back to the sendPacket sendPacketStream << keyListString; QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); QStringList valueList; foreach (const QString& dataKey, keyList) { qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); if (reply->len) { // copy the value that redis returned valueList << QString(reply->str); } else { // didn't find a value - insert a space valueList << QChar(' '); } freeReplyObject(reply); } // append the value QStringList using the right separator sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR); } else {
void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "OctreePacketProcessor::processPacket()"); QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } int messageLength = mutablePacket.size(); Application* app = Application::getInstance(); bool wasStatsPacket = false; PacketType voxelPacketType = packetTypeForPacket(mutablePacket); // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet if (voxelPacketType == PacketTypeOctreeStats) { int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode); wasStatsPacket = true; if (messageLength > statsMessageLength) { mutablePacket = mutablePacket.mid(statsMessageLength); // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but // this is testing the version and hash of the original packet if (!DependencyManager::get<NodeList>()->packetVersionAndHashMatch(packet)) { return; // bail since piggyback data doesn't match our versioning } } else { // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket() return; // bail since no piggyback data } } // fall through to piggyback message voxelPacketType = packetTypeForPacket(mutablePacket); PacketVersion packetVersion = mutablePacket[1]; PacketVersion expectedVersion = versionForPacketType(voxelPacketType); // check version of piggyback packet against expected version if (packetVersion != expectedVersion) { static QMultiMap<QUuid, PacketType> versionDebugSuppressMap; QUuid senderUUID = uuidFromPacketHeader(packet); if (!versionDebugSuppressMap.contains(senderUUID, voxelPacketType)) { qDebug() << "Packet version mismatch on" << voxelPacketType << "- Sender" << senderUUID << "sent" << (int)packetVersion << "but" << (int)expectedVersion << "expected."; emit packetVersionMismatch(); versionDebugSuppressMap.insert(senderUUID, voxelPacketType); } return; // bail since piggyback version doesn't match } app->trackIncomingOctreePacket(mutablePacket, sendingNode, wasStatsPacket); if (sendingNode) { switch(voxelPacketType) { case PacketTypeEntityErase: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { app->_entities.processEraseMessage(mutablePacket, sendingNode); } } break; case PacketTypeEntityData: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { app->_entities.processDatagram(mutablePacket, sendingNode); } } break; case PacketTypeEnvironmentData: { app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket); } break; default: { // nothing to do } break; } } }
void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "OctreePacketProcessor::processPacket()"); const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && qApp->getLogger()->extraDebugging()) { qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } bool wasStatsPacket = false; PacketType octreePacketType = message->getType(); // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet if (octreePacketType == PacketType::OctreeStats) { int statsMessageLength = qApp->processOctreeStats(*message, sendingNode); wasStatsPacket = true; int piggybackBytes = message->getSize() - statsMessageLength; if (piggybackBytes) { // construct a new packet from the piggybacked one auto buffer = std::unique_ptr<char[]>(new char[piggybackBytes]); memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggybackBytes); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, message->getSenderSockAddr()); message = QSharedPointer<ReceivedMessage>::create(*newPacket); } else { // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket() return; // bail since no piggyback data } } // fall through to piggyback message PacketType packetType = message->getType(); // check version of piggyback packet against expected version if (message->getVersion() != versionForPacketType(message->getType())) { static QMultiMap<QUuid, PacketType> versionDebugSuppressMap; const QUuid& senderUUID = message->getSourceID(); if (!versionDebugSuppressMap.contains(senderUUID, packetType)) { qDebug() << "Was stats packet? " << wasStatsPacket; qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender" << senderUUID << "sent" << (int) message->getVersion() << "but" << (int) versionForPacketType(packetType) << "expected."; emit packetVersionMismatch(); versionDebugSuppressMap.insert(senderUUID, packetType); } return; // bail since piggyback version doesn't match } qApp->trackIncomingOctreePacket(*message, sendingNode, wasStatsPacket); // seek back to beginning of packet after tracking message->seek(0); switch(packetType) { case PacketType::EntityErase: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { qApp->getEntities()->processEraseMessage(*message, sendingNode); } } break; case PacketType::EntityData: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { qApp->getEntities()->processDatagram(*message, sendingNode); } } break; default: { // nothing to do } break; } }