// // NLPacket.cpp // libraries/networking/src // // Created by Clement on 7/6/15. // Copyright 2015 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 "NLPacket.h" #include "HMACAuth.h" int NLPacket::localHeaderSize(PacketType type) { bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type); bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type); qint64 optionalSize = (nonSourced ? 0 : NUM_BYTES_LOCALID) + ((nonSourced || nonVerified) ? 0 : NUM_BYTES_MD5_HASH); return sizeof(PacketType) + sizeof(PacketVersion) + optionalSize; } int NLPacket::totalHeaderSize(PacketType type, bool isPartOfMessage) { return Packet::totalHeaderSize(isPartOfMessage) + NLPacket::localHeaderSize(type); } int NLPacket::maxPayloadSize(PacketType type, bool isPartOfMessage) { return Packet::maxPayloadSize(isPartOfMessage) - NLPacket::localHeaderSize(type); } std::unique_ptr NLPacket::create(PacketType type, qint64 size, bool isReliable, bool isPartOfMessage, PacketVersion version) { auto packet = std::unique_ptr(new NLPacket(type, size, isReliable, isPartOfMessage, version)); packet->open(QIODevice::ReadWrite); return packet; } std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with null data Q_ASSERT(data); // Fail with invalid size Q_ASSERT(size >= 0); // allocate memory auto packet = std::unique_ptr(new NLPacket(std::move(data), size, senderSockAddr)); packet->open(QIODevice::ReadOnly); return packet; } std::unique_ptr NLPacket::fromBase(std::unique_ptr packet) { // Fail with null packet Q_ASSERT(packet); // call our constructor to create an NLPacket from this Packet return std::unique_ptr(new NLPacket(std::move(*packet))); } std::unique_ptr NLPacket::createCopy(const NLPacket& other) { return std::unique_ptr(new NLPacket(other)); } NLPacket::NLPacket(PacketType type, qint64 size, bool isReliable, bool isPartOfMessage, PacketVersion version) : Packet((size == -1) ? -1 : NLPacket::localHeaderSize(type) + size, isReliable, isPartOfMessage), _type(type), _version((version == 0) ? versionForPacketType(type) : version) { adjustPayloadStartAndCapacity(NLPacket::localHeaderSize(_type)); writeTypeAndVersion(); } NLPacket::NLPacket(Packet&& packet) : Packet(std::move(packet)) { readType(); readVersion(); readSourceID(); adjustPayloadStartAndCapacity(NLPacket::localHeaderSize(_type), _payloadSize > 0); } NLPacket::NLPacket(const NLPacket& other) : Packet(other) { _type = other._type; _version = other._version; _sourceID = other._sourceID; } NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : Packet(std::move(data), size, senderSockAddr) { // sanity check before we decrease the payloadSize with the payloadCapacity Q_ASSERT(_payloadSize == _payloadCapacity); readType(); readVersion(); readSourceID(); adjustPayloadStartAndCapacity(NLPacket::localHeaderSize(_type), _payloadSize > 0); } NLPacket::NLPacket(NLPacket&& other) : Packet(std::move(other)) { _type = other._type; _version = other._version; _sourceID = std::move(other._sourceID); } NLPacket& NLPacket::operator=(const NLPacket& other) { Packet::operator=(other); _type = other._type; _version = other._version; _sourceID = other._sourceID; return *this; } NLPacket& NLPacket::operator=(NLPacket&& other) { Packet::operator=(std::move(other)); _type = other._type; _version = other._version; _sourceID = std::move(other._sourceID); return *this; } PacketType NLPacket::typeInHeader(const udt::Packet& packet) { auto headerOffset = Packet::totalHeaderSize(packet.isPartOfMessage()); return *reinterpret_cast(packet.getData() + headerOffset); } PacketVersion NLPacket::versionInHeader(const udt::Packet& packet) { auto headerOffset = Packet::totalHeaderSize(packet.isPartOfMessage()); return *reinterpret_cast(packet.getData() + headerOffset + sizeof(PacketType)); } NLPacket::LocalID NLPacket::sourceIDInHeader(const udt::Packet& packet) { int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion); return *reinterpret_cast(packet.getData() + offset); } QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) { int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID; return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH); } QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) { int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH; // add the packet payload and the connection UUID HMACAuth::HMACHash hashResult; if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) { return QByteArray(); } return QByteArray((const char*) hashResult.data(), (int) hashResult.size()); } void NLPacket::writeTypeAndVersion() { auto headerOffset = Packet::totalHeaderSize(isPartOfMessage()); // Pack the packet type memcpy(_packet.get() + headerOffset, &_type, sizeof(PacketType)); // Pack the packet version memcpy(_packet.get() + headerOffset + sizeof(PacketType), &_version, sizeof(_version)); } 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(); } void NLPacket::setVersion(PacketVersion version) { _version = version; writeTypeAndVersion(); } void NLPacket::readType() { _type = NLPacket::typeInHeader(*this); } void NLPacket::readVersion() { _version = NLPacket::versionInHeader(*this); } void NLPacket::readSourceID() { if (PacketTypeEnum::getNonSourcedPackets().contains(_type)) { _sourceID = NULL_LOCAL_ID; } else { _sourceID = sourceIDInHeader(*this); } } void NLPacket::writeSourceID(LocalID sourceID) const { Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion); memcpy(_packet.get() + offset, &sourceID, sizeof(sourceID)); _sourceID = sourceID; } void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const { Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) && !PacketTypeEnum::getNonVerifiedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID; QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth); memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size()); }