PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
QueryHandler.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
26 namespace pocketmine\network\query;
27 
32 
34  private $server, $lastToken, $token, $longData, $shortData, $timeout;
35 
36  const HANDSHAKE = 9;
37  const STATISTICS = 0;
38 
39  public function __construct(){
40  $this->server = Server::getInstance();
41  $this->server->getLogger()->info("Starting GS4 status listener");
42  $addr = ($ip = $this->server->getIp()) != "" ? $ip : "0.0.0.0";
43  $port = $this->server->getPort();
44  $this->server->getLogger()->info("Setting query port to $port");
45  /*
46  The Query protocol is built on top of the existing Minecraft PE UDP network stack.
47  Because the 0xFE packet does not exist in the MCPE protocol,
48  we can identify Query packets and remove them from the packet queue.
49 
50  Then, the Query class handles itself sending the packets in raw form, because
51  packets can conflict with the MCPE ones.
52  */
53 
54  $this->regenerateToken();
55  $this->lastToken = $this->token;
56  $this->regenerateInfo();
57  $this->server->getLogger()->info("Query running on $addr:$port");
58  }
59 
60  public function regenerateInfo(){
61  $this->server->getPluginManager()->callEvent($ev = new QueryRegenerateEvent($this->server, 5));
62  $this->longData = $ev->getLongQuery();
63  $this->shortData = $ev->getShortQuery();
64  $this->timeout = microtime(true) + $ev->getTimeout();
65  }
66 
67  public function regenerateToken(){
68  $this->lastToken = $this->token;
69  $this->token = @Utils::getRandomBytes(16, false);
70  }
71 
72  public static function getTokenString($token, $salt){
73  return Binary::readInt(substr(hash("sha512", $salt . ":" . $token, true), 7, 4));
74  }
75 
76  public function handle($address, $port, $packet){
77  $offset = 2;
78  $packetType = ord($packet{$offset++});
79  $sessionID = Binary::readInt(substr($packet, $offset, 4));
80  $offset += 4;
81  $payload = substr($packet, $offset);
82 
83  switch($packetType){
84  case self::HANDSHAKE: //Handshake
85  $reply = chr(self::HANDSHAKE);
86  $reply .= Binary::writeInt($sessionID);
87  $reply .= self::getTokenString($this->token, $address) . "\x00";
88 
89  $this->server->sendPacket($address, $port, $reply);
90  break;
91  case self::STATISTICS: //Stat
92  $token = Binary::readInt(substr($payload, 0, 4));
93  if($token !== self::getTokenString($this->token, $address) and $token !== self::getTokenString($this->lastToken, $address)){
94  break;
95  }
96  $reply = chr(self::STATISTICS);
97  $reply .= Binary::writeInt($sessionID);
98 
99  if($this->timeout < microtime(true)){
100  $this->regenerateInfo();
101  }
102 
103  if(strlen($payload) === 8){
104  $reply .= $this->longData;
105  }else{
106  $reply .= $this->shortData;
107  }
108  $this->server->sendPacket($address, $port, $reply);
109  break;
110  }
111  }
112 
113 }
static getInstance()
Definition: Server.php:1444
static getRandomBytes($length=16, $secure=true, $raw=true, $startEntropy="", &$rounds=0, &$drop=0)
Definition: Utils.php:223