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 OctreeEditPacketSender::processNackPacket(const QByteArray& packet) { // parse sending node from packet, retrieve packet history for that node QUuid sendingNodeUUID = uuidFromPacketHeader(packet); // if packet history doesn't exist for the sender node (somehow), bail if (!_sentPacketHistories.contains(sendingNodeUUID)) { return; } const SentPacketHistory& sentPacketHistory = _sentPacketHistories.value(sendingNodeUUID); int numBytesPacketHeader = numBytesForPacketHeader(packet); const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader; // read number of sequence numbers uint16_t numSequenceNumbers = (*(uint16_t*)dataAt); dataAt += sizeof(uint16_t); // read sequence numbers and queue packets for resend for (int i = 0; i < numSequenceNumbers; i++) { unsigned short int sequenceNumber = (*(unsigned short int*)dataAt); dataAt += sizeof(unsigned short int); // retrieve packet from history const QByteArray* packet = sentPacketHistory.getPacket(sequenceNumber); if (packet) { const SharedNodePointer& node = DependencyManager::get<NodeList>()->nodeWithUUID(sendingNodeUUID); queuePacketForSending(node, *packet); } } }
int NodeList::processDomainServerList(const QByteArray& packet) { // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { _domainHandler.setUUID(uuidFromPacketHeader(packet)); _domainHandler.setIsConnected(true); } int readNodes = 0; // setup variables to read into from QDataStream qint8 nodeType; QUuid nodeUUID, connectionUUID; HifiSockAddr nodePublicSocket; HifiSockAddr nodeLocalSocket; QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); // pull our owner UUID from the packet, it's always the first thing QUuid newUUID; packetStream >> newUUID; setSessionUUID(newUUID); // pull each node in the packet while(packetStream.device()->pos() < packet.size()) { packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket; // if the public socket address is 0 then it's reachable at the same IP // as the domain server if (nodePublicSocket.getAddress().isNull()) { nodePublicSocket.setAddress(_domainHandler.getIP()); } SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket); packetStream >> connectionUUID; node->setConnectionSecret(connectionUUID); } // ping inactive nodes in conjunction with receipt of list from domain-server // this makes it happen every second and also pings any newly added nodes pingInactiveNodes(); return readNodes; }
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 OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { bool extraDebugging = false; if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram()"; } if (!_tree) { qCDebug(octree) << "OctreeRenderer::processDatagram() called before init, calling init()..."; this->init(); } bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails); unsigned int packetLength = dataByteArray.size(); PacketType command = packetTypeForPacket(dataByteArray); unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); PacketType expectedType = getExpectedPacketType(); // packetVersion is the second byte PacketVersion packetVersion = dataByteArray[1]; if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. _tree->setIsViewing(true); const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(dataByteArray.data()) + numBytesPacketHeader; OCTREE_PACKET_FLAGS flags = (*(OCTREE_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0; int flightTime = arrivedAt - sentAt + clockSkew; OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; unsigned int dataBytes = packetLength - (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE); if (extraDebugging) { qCDebug(octree, "OctreeRenderer::processDatagram() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%u data:%u", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, packetLength, dataBytes); } int subsection = 1; while (dataBytes > 0) { if (packetIsCompressed) { if (dataBytes > sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE)) { sectionLength = (*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)dataAt); dataAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); dataBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } else { sectionLength = 0; dataBytes = 0; // stop looping something is wrong } } else { sectionLength = dataBytes; } if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, sourceUUID, sourceNode, false, packetVersion); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); if (extraDebugging) { qCDebug(octree, "OctreeRenderer::processDatagram() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%u data:%u" " subsection:%d sectionLength:%d uncompressed:%d", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, packetLength, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); } if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* START _tree->readBitstreamToTree()..."; } _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* END _tree->readBitstreamToTree()..."; } _tree->unlock(); dataBytes -= sectionLength; dataAt += sectionLength; } } subsection++; } }
SharedNodePointer LimitedNodeList::sendingNodeForPacket(const QByteArray& packet) { QUuid nodeUUID = uuidFromPacketHeader(packet); // return the matching node, or NULL if there is no match return nodeWithUUID(nodeUUID); }
int InboundAudioStream::parseData(const QByteArray& packet) { PacketType packetType = packetTypeForPacket(packet); QUuid senderUUID = uuidFromPacketHeader(packet); // parse header int numBytesHeader = numBytesForPacketHeader(packet); const char* sequenceAt = packet.constData() + numBytesHeader; int readBytes = numBytesHeader; // parse sequence number and track it quint16 sequence = *(reinterpret_cast<const quint16*>(sequenceAt)); readBytes += sizeof(quint16); SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence, senderUUID); frameReceivedUpdateTimingStats(); // TODO: handle generalized silent packet here????? // parse the info after the seq number and before the audio data.(the stream properties) int numAudioSamples; readBytes += parseStreamProperties(packetType, packet.mid(readBytes), numAudioSamples); // handle this packet based on its arrival status. // For now, late packets are ignored. It may be good in the future to insert the late audio frame // into the ring buffer to fill in the missing frame if it hasn't been mixed yet. switch (arrivalInfo._status) { case SequenceNumberStats::Early: { int packetsDropped = arrivalInfo._seqDiffFromExpected; writeSamplesForDroppedPackets(packetsDropped * numAudioSamples); // fall through to OnTime case } case SequenceNumberStats::OnTime: { readBytes += parseAudioData(packetType, packet.mid(readBytes), numAudioSamples); break; } default: { break; } } int framesAvailable = _ringBuffer.framesAvailable(); // if this stream was starved, check if we're still starved. if (_isStarved && framesAvailable >= _desiredJitterBufferFrames) { _isStarved = false; } // if the ringbuffer exceeds the desired size by more than the threshold specified, // drop the oldest frames so the ringbuffer is down to the desired size. if (framesAvailable > _desiredJitterBufferFrames + _maxFramesOverDesired) { int framesToDrop = framesAvailable - (_desiredJitterBufferFrames + DESIRED_JITTER_BUFFER_FRAMES_PADDING); _ringBuffer.shiftReadPosition(framesToDrop * _ringBuffer.getNumFrameSamples()); _framesAvailableStat.reset(); _currentJitterBufferFrames = 0; _oldFramesDropped += framesToDrop; } framesAvailableChanged(); return readBytes; }
void IceServer::processDatagrams() { HifiSockAddr sendingSockAddr; QByteArray incomingPacket; while (_serverSocket.hasPendingDatagrams()) { incomingPacket.resize(_serverSocket.pendingDatagramSize()); _serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(), sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); if (packetTypeForPacket(incomingPacket) == PacketTypeIceServerHeartbeat) { QUuid senderUUID = uuidFromPacketHeader(incomingPacket); // pull the public and private sock addrs for this peer HifiSockAddr publicSocket, localSocket; QDataStream hearbeatStream(incomingPacket); hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket)); hearbeatStream >> publicSocket >> localSocket; // make sure we have this sender in our peer hash SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); if (!matchingPeer) { // if we don't have this sender we need to create them now matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket)); _activePeers.insert(senderUUID, matchingPeer); qDebug() << "Added a new network peer" << *matchingPeer; } else { // we already had the peer so just potentially update their sockets matchingPeer->setPublicSocket(publicSocket); matchingPeer->setLocalSocket(localSocket); qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer; } // update our last heard microstamp for this network peer to now matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); // check if this node also included a UUID that they would like to connect to QUuid connectRequestID; hearbeatStream >> connectRequestID; // get the peers asking for connections with this peer QSet<QUuid>& requestingConnections = _currentConnections[senderUUID]; if (!connectRequestID.isNull()) { qDebug() << "Peer wants to connect to peer with ID" << uuidStringWithoutCurlyBraces(connectRequestID); // ensure this peer is in the set of current connections for the peer with ID it wants to connect with _currentConnections[connectRequestID].insert(senderUUID); // add the ID of the node they have said they would like to connect to requestingConnections.insert(connectRequestID); } if (requestingConnections.size() > 0) { // send a heartbeart response based on the set of connections qDebug() << "Sending a heartbeat response to" << senderUUID << "who has" << requestingConnections.size() << "potential connections"; sendHeartbeatResponse(sendingSockAddr, requestingConnections); } } }