From e8cbb427be8aa8165d9b3ab350a304752f9a70f7 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sun, 13 Jul 2014 12:40:05 +0200 Subject: [PATCH] First commit --- .idea/.name | 1 + .idea/PacketLogger.iml | 19 ++ .idea/encodings.xml | 5 + .idea/misc.xml | 5 + .idea/modules.xml | 9 + .idea/scopes/scope_settings.xml | 5 + .idea/vcs.xml | 7 + .idea/workspace.xml | 342 +++++++++++++++++++++ LICENSE | 20 ++ plugin.yml | 5 + resources/config.yml | 23 ++ src/shoghicp/PacketLogger/PacketLogger.php | 223 ++++++++++++++ 12 files changed, 664 insertions(+) create mode 100644 .idea/.name create mode 100644 .idea/PacketLogger.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 LICENSE create mode 100644 plugin.yml create mode 100644 resources/config.yml create mode 100644 src/shoghicp/PacketLogger/PacketLogger.php diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..90de8d6 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +PacketLogger \ No newline at end of file diff --git a/.idea/PacketLogger.iml b/.idea/PacketLogger.iml new file mode 100644 index 0000000..858a20d --- /dev/null +++ b/.idea/PacketLogger.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1162f43 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7d8393a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..bf9819b --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1405186988136 + 1405186988136 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..011eb9d --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright (c) 2014 shoghicp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..89f9958 --- /dev/null +++ b/plugin.yml @@ -0,0 +1,5 @@ +name: PacketLogger +author: shoghicp +api: 1.0.0 +version: 1.0.0 +main: shoghicp\PacketLogger\PacketLogger \ No newline at end of file diff --git a/resources/config.yml b/resources/config.yml new file mode 100644 index 0000000..5604635 --- /dev/null +++ b/resources/config.yml @@ -0,0 +1,23 @@ +#Configuration file for PacketLogger + +#variables available: {name}, {clientId}, {ip}, {time} +logName: "{name}_{clientId}-{time}.log" + +#These selectors will match when ANY of the conditions is met +selectors: + mode: match #When match, it will allow when a selector is matched. When refuse, it'll log when there is no match + name: #case insensitive + - shoghicp + - steve + + clientId: + - 123456 + + ip: + - 192.168.0.1 + +#These filters decide what packets will be logged +filters: + packetId: #Filter by the packet id (faster) + default: true #Default value of the packets not specified here. true will log it, false will not + 0xba: false #Example: FullChunkDataPacket won't be logged diff --git a/src/shoghicp/PacketLogger/PacketLogger.php b/src/shoghicp/PacketLogger/PacketLogger.php new file mode 100644 index 0000000..fa7f6bb --- /dev/null +++ b/src/shoghicp/PacketLogger/PacketLogger.php @@ -0,0 +1,223 @@ +saveDefaultConfig(); + $config = $this->getConfig(); + $selectors = $config->get("selectors"); + + if($config->exists("logName")){ + $this->logName = $config->get("logName"); + } + + $this->mode = isset($selectors["mode"]) ? (strtolower($selectors["mode"]) === "refuse" ? false : true) : true; + + if(isset($selectors["name"])){ + foreach($selectors["name"] as $name){ + $this->nameSelector[strtolower($name)] = true; + } + } + + if(isset($selectors["clientId"])){ + foreach($selectors["clientId"] as $clientId){ + $this->clientIdSelector[(int) $clientId] = true; + } + } + + if(isset($selectors["ip"])){ + foreach($selectors["ip"] as $ip){ + $this->ipSelector[$ip] = true; + } + } + + $filters = $config->get("filters"); + if(isset($filters["packetId"])){ + $this->packetIdDefault = (bool) $filters["packetId"]["default"]; + unset($filters["packetId"]["default"]); + foreach($filters["packetId"] as $packetId => $allow){ + $this->packetIdFilter[(int) $packetId] = (bool) $allow; + } + } + + $this->getServer()->getPluginManager()->registerEvents($this, $this); + } + + public function onDisable(){ + foreach($this->getServer()->getOnlinePlayers() as $player){ + if(isset($this->sessions[spl_object_hash($player)])){ + $this->closeSession($player); + } + } + } + + private function isPacketAllowed(DataPacket $packet){ + if(isset($this->packetIdFilter[$packet->pid()])){ + return $this->packetIdFilter[$packet->pid()]; + } + return $this->packetIdDefault; + } + + private function logInboundPacket(Player $player, DataPacket $packet){ + if($this->isPacketAllowed($packet)){ + $packet->encode(); //other plugins might have changed the packet + $header = "[Client -> Server 0x".dechex($packet->pid())."] ".(new \ReflectionClass($packet))->getShortName() . " (length ".strlen($packet->buffer).")"; + $fields = $this->getFields($packet); + $binary = trim(Utils::hexdump($packet->buffer)); + fwrite($this->sessions[spl_object_hash($player)], $header . PHP_EOL . $binary . PHP_EOL . $fields . PHP_EOL . PHP_EOL . PHP_EOL); + } + } + + private function logOutboundPacket(Player $player, DataPacket $packet){ + if($this->isPacketAllowed($packet)){ + $packet->encode(); //needed :( + $header = "[Server -> Client 0x".dechex($packet->pid())."] ".(new \ReflectionClass($packet))->getShortName() . " (length ".strlen($packet->buffer).")"; + $fields = $this->getFields($packet); + $binary = trim(Utils::hexdump($packet->buffer)); + fwrite($this->sessions[spl_object_hash($player)], $header . PHP_EOL . $binary . PHP_EOL . $fields . PHP_EOL . PHP_EOL . PHP_EOL); + } + } + + private static function safePrint($value, $spaces = 2){ + if(is_object($value)){ + if((new \ReflectionClass($value))->hasMethod("__toString")){ + $value = $value->__toString(); + }else{ + $value = get_class($value); + } + }elseif(is_string($value)){ + if(!Utils::printable($value)){ + $value = "0x".bin2hex($value); + } + }elseif(is_array($value)){ + $d = "Array:"; + foreach($value as $key => $v){ + $d .= PHP_EOL . str_repeat(" ", $spaces) . "$key: " . self::safePrint($v, $spaces + 1); + } + $value = $d; + }else{ + $value = trim(str_replace("\n", "\n ", print_r($value, true))); + } + + return $value; + } + + private function getFields(DataPacket $packet){ + $output = ""; + foreach($packet as $key => $value){ + if($key === "buffer"){ + continue; + } + + $output .= " $key: " . self::safePrint($value) . PHP_EOL; + } + + return rtrim($output); + } + + private function writeHeader($fp, $name, $clientId, Player $player){ + fwrite($fp, "# Log generated by PacketLogger ".$this->getDescription()->getVersion() + ." in PocketMine-MP ".$this->getServer()->getPocketMineVersion() ." for Minecraft: PE ".$this->getServer()->getVersion() + ." (protocol #".Info::CURRENT_PROTOCOL.")" . PHP_EOL . PHP_EOL); + fwrite($fp, "Player: $name, clientId $clientId from [/".$player->getAddress().":".$player->getPort() . PHP_EOL); + fwrite($fp, "Start time: ".date("c") . PHP_EOL . PHP_EOL); + } + + private function closeSession(Player $player){ + fwrite($this->sessions[$i = spl_object_hash($player)], PHP_EOL . PHP_EOL . "End time: ". date("c")); + fclose($this->sessions[$i]); + unset($this->sessions[$i]); + } + + /** + * @param PlayerQuitEvent $event + * + * @priority NORMAL + */ + public function onPlayerQuit(PlayerQuitEvent $event){ + if(isset($this->sessions[spl_object_hash($event->getPlayer())])){ + $this->closeSession($event->getPlayer()); + } + } + + /** + * @param DataPacketReceiveEvent $event + * + * @priority MONITOR + * @ignoreCancelled true + */ + public function onInboundPacket(DataPacketReceiveEvent $event){ + $player = $event->getPlayer(); + $packet = $event->getPacket(); + if(isset($this->sessions[spl_object_hash($player)])){ + $this->logInboundPacket($player, $packet); + }elseif($packet instanceof LoginPacket){ + $name = strtolower($packet->username); + $clientId = $packet->clientId; + $ip = $player->getAddress(); + + $match = false; + if(isset($this->nameSelector[$name])){ + $match = true; + }elseif(isset($this->clientIdSelector[$clientId])){ + $match = true; + }elseif(isset($this->ipSelector[$ip])){ + $match = true; + } + + if($match === $this->mode){ + $path = $this->getDataFolder() . str_replace([ + "{name}", + "{clientId}", + "{ip}", + "{time}" + ], [ + $name, + $clientId, + $ip, + time() + ], $this->logName); + $this->sessions[spl_object_hash($player)] = fopen($path, "w+b"); + $this->writeHeader($this->sessions[spl_object_hash($player)], $name, $clientId, $player); + $this->logInboundPacket($player, $packet); + } + } + } + + /** + * @param DataPacketSendEvent $event + * + * @priority MONITOR + * @ignoreCancelled true + */ + public function onOutboundPacket(DataPacketSendEvent $event){ + $player = $event->getPlayer(); + if(isset($this->sessions[spl_object_hash($player)])){ + $this->logOutboundPacket($player, $event->getPacket()); + } + } +} \ No newline at end of file