Example #1
0
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;
}
Example #2
0
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));
}
Example #3
0
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();
}
Example #4
0
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();
}
Example #5
0
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;
    }
}
Example #6
0
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;
}
Example #7
0
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();
    });
}
Example #8
0
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;
}
Example #9
0
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 {
Example #10
0
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;
    }
}