diff --git a/src/network/FilterNoisyPacketException.php b/src/network/FilterNoisyPacketException.php new file mode 100644 index 000000000..878feebe7 --- /dev/null +++ b/src/network/FilterNoisyPacketException.php @@ -0,0 +1,31 @@ +repeatedPacketFilters[$packetId])){ - if($buffer === $this->repeatedPacketFilters[$packetId]){ - $this->repeatedPacketFilterStats[$packetId]++; - return true; - } - - $this->repeatedPacketFilters[$packetId] = $buffer; + if($buffer === $this->noisyPacketBuffer){ + $this->noisyPacketsDropped++; + return true; } + //stop filtering once we see a packet with a different buffer + //this won't be any good for interleaved spammy packets, but we haven't seen any of those so far, and this + //is the simplest and most conservative filter we can do + $this->noisyPacketBuffer = ""; + $this->noisyPacketsDropped = 0; return false; } @@ -453,9 +453,17 @@ class NetworkSession{ $decompressed = $payload; } + $count = 0; try{ $stream = new BinaryStream($decompressed); foreach(PacketBatch::decodeRaw($stream) as $buffer){ + if(++$count >= self::INCOMING_PACKET_BATCH_HARD_LIMIT){ + //this should be well more than enough; under normal conditions the game packet rate limiter + //will kick in well before this. This is only here to make sure we can't get huge batches of + //noisy packets to bog down the server, since those aren't counted by the regular limiter. + throw new PacketHandlingException("Reached hard limit of " . self::INCOMING_PACKET_BATCH_HARD_LIMIT . " per batch packet"); + } + if($this->checkRepeatedPacketFilter($buffer)){ continue; } @@ -471,6 +479,8 @@ class NetworkSession{ }catch(PacketHandlingException $e){ $this->logger->debug($packet->getName() . ": " . base64_encode($buffer)); throw PacketHandlingException::wrap($e, "Error processing " . $packet->getName()); + }catch(FilterNoisyPacketException){ + $this->noisyPacketBuffer = $buffer; } if(!$this->isConnected()){ //handling this packet may have caused a disconnection diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index ba76aec52..e9c6556ef 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -44,6 +44,7 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\network\FilterNoisyPacketException; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\ActorEventPacket; @@ -473,6 +474,22 @@ class InGamePacketHandler extends PacketHandler{ switch($data->getActionType()){ case UseItemTransactionData::ACTION_CLICK_BLOCK: + //TODO: start hack for client spam bug + $clickPos = $data->getClickPosition(); + $spamBug = ($this->lastRightClickData !== null && + microtime(true) - $this->lastRightClickTime < 0.1 && //100ms + $this->lastRightClickData->getPlayerPosition()->distanceSquared($data->getPlayerPosition()) < 0.00001 && + $this->lastRightClickData->getBlockPosition()->equals($data->getBlockPosition()) && + $this->lastRightClickData->getClickPosition()->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error + ); + //get rid of continued spam if the player clicks and holds right-click + $this->lastRightClickData = $data; + $this->lastRightClickTime = microtime(true); + if($spamBug){ + throw new FilterNoisyPacketException(); + } + //TODO: end hack for client spam bug + self::validateFacing($data->getFace()); $blockPos = $data->getBlockPosition(); @@ -724,7 +741,9 @@ class InGamePacketHandler extends PacketHandler{ } public function handleAnimate(AnimatePacket $packet) : bool{ - return true; //Not used + //this spams harder than a firehose on left click if "Improved Input Response" is enabled, and we don't even + //use it anyway :< + throw new FilterNoisyPacketException(); } public function handleContainerClose(ContainerClosePacket $packet) : bool{