diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 6503ee95..1d31017a 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -2031,7 +2031,7 @@ public: /** CMSG_BUY_ITEM packet builder */ class BuyItemPacket { public: - static network::Packet build(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count); + static network::Packet build(uint64_t vendorGuid, uint32_t itemId, uint32_t count); }; /** CMSG_SELL_ITEM packet builder */ diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 3ccd2e96..0709a63e 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -8236,7 +8236,8 @@ void GameHandler::closeVendor() { void GameHandler::buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count) { if (state != WorldState::IN_WORLD || !socket) return; - auto packet = BuyItemPacket::build(vendorGuid, itemId, slot, count); + (void)slot; + auto packet = BuyItemPacket::build(vendorGuid, itemId, count); socket->send(packet); } diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 9973c18e..735f2871 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3153,13 +3153,11 @@ network::Packet ListInventoryPacket::build(uint64_t npcGuid) { return packet; } -network::Packet BuyItemPacket::build(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count) { +network::Packet BuyItemPacket::build(uint64_t vendorGuid, uint32_t itemId, uint32_t count) { network::Packet packet(wireOpcode(Opcode::CMSG_BUY_ITEM)); packet.writeUInt64(vendorGuid); packet.writeUInt32(itemId); // item entry - packet.writeUInt32(slot); // vendor slot (1-based position in vendor list) packet.writeUInt32(count); - packet.writeUInt8(0); // bag slot (0 = find any available bag slot) return packet; } @@ -3172,6 +3170,12 @@ network::Packet SellItemPacket::build(uint64_t vendorGuid, uint64_t itemGuid, ui } bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data) { + data = ListInventoryData{}; + if (packet.getSize() - packet.getReadPos() < 9) { + LOG_WARNING("ListInventoryParser: packet too short"); + return false; + } + data.vendorGuid = packet.readUInt64(); uint8_t itemCount = packet.readUInt8(); @@ -3184,10 +3188,25 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data // Some servers omit the extendedCost field entirely; reading 8 fields on a 7-field packet // misaligns every item after the first and produces garbage prices. size_t remaining = packet.getSize() - packet.getReadPos(); - bool hasExtendedCost = (remaining >= static_cast(itemCount) * 32); + const size_t bytesPerItemNoExt = 28; + const size_t bytesPerItemWithExt = 32; + bool hasExtendedCost = false; + if (remaining < static_cast(itemCount) * bytesPerItemNoExt) { + LOG_WARNING("ListInventoryParser: truncated packet (items=", (int)itemCount, + ", remaining=", remaining, ")"); + return false; + } + if (remaining >= static_cast(itemCount) * bytesPerItemWithExt) { + hasExtendedCost = true; + } data.items.reserve(itemCount); for (uint8_t i = 0; i < itemCount; ++i) { + const size_t perItemBytes = hasExtendedCost ? bytesPerItemWithExt : bytesPerItemNoExt; + if (packet.getSize() - packet.getReadPos() < perItemBytes) { + LOG_WARNING("ListInventoryParser: item ", (int)i, " truncated"); + return false; + } VendorItem item; item.slot = packet.readUInt32(); item.itemId = packet.readUInt32();