PocketMine-MP 5.35.1 git-e32e836dad793a3a3c8ddd8927c00e112b1e576a
Loading...
Searching...
No Matches
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
22declare(strict_types=1);
23
29
30use pmmp\encoding\BE;
31use pmmp\encoding\Byte;
32use pmmp\encoding\ByteBufferReader;
33use pmmp\encoding\ByteBufferWriter;
34use pmmp\encoding\DataDecodeException;
38use function hash;
39use function random_bytes;
40use function substr;
41
43 private string $lastToken;
44 private string $token;
45
46 private \Logger $logger;
47
48 public const HANDSHAKE = 9;
49 public const STATISTICS = 0;
50
51 public function __construct(
52 private Server $server
53 ){
54 $this->logger = new \PrefixedLogger($this->server->getLogger(), "Query Handler");
55
56 /*
57 The Query protocol is built on top of the existing Minecraft PE UDP network stack.
58 Because the 0xFE packet does not exist in the MCPE protocol,
59 we can identify Query packets and remove them from the packet queue.
60
61 Then, the Query class handles itself sending the packets in raw form, because
62 packets can conflict with the MCPE ones.
63 */
64
65 $this->token = $this->generateToken();
66 $this->lastToken = $this->token;
67 }
68
69 public function getPattern() : string{
70 return '/^\xfe\xfd.+$/s';
71 }
72
73 private function generateToken() : string{
74 return random_bytes(16);
75 }
76
77 public function regenerateToken() : void{
78 $this->lastToken = $this->token;
79 $this->token = $this->generateToken();
80 }
81
82 public static function getTokenString(string $token, string $salt) : int{
83 return BE::unpackSignedInt(substr(hash("sha512", $salt . ":" . $token, true), 7, 4));
84 }
85
86 public function handle(AdvancedNetworkInterface $interface, string $address, int $port, string $packet) : bool{
87 try{
88 $stream = new ByteBufferReader($packet);
89 $header = $stream->readByteArray(2);
90 if($header !== "\xfe\xfd"){ //TODO: have this filtered by the regex filter we installed above
91 return false;
92 }
93 $packetType = Byte::readUnsigned($stream);
94 $sessionID = BE::readUnsignedInt($stream);
95
96 switch($packetType){
97 case self::HANDSHAKE: //Handshake
98 $writer = new ByteBufferWriter();
99 Byte::writeUnsigned($writer, self::HANDSHAKE);
100 BE::writeUnsignedInt($writer, $sessionID);
101 $writer->writeByteArray(self::getTokenString($this->token, $address) . "\x00");
102
103 $interface->sendRawPacket($address, $port, $writer->getData());
104
105 return true;
106 case self::STATISTICS: //Stat
107 $token = BE::readUnsignedInt($stream);
108 if($token !== ($t1 = self::getTokenString($this->token, $address)) && $token !== ($t2 = self::getTokenString($this->lastToken, $address))){
109 $this->logger->debug("Bad token $token from $address $port, expected $t1 or $t2");
110
111 return true;
112 }
113 $writer = new ByteBufferWriter();
114 Byte::writeUnsigned($writer, self::STATISTICS);
115 BE::writeUnsignedInt($writer, $sessionID);
116
117 $remaining = $stream->getUnreadLength();
118 if($remaining === 4){ //TODO: check this! according to the spec, this should always be here and always be FF FF FF 01
119 $writer->writeByteArray($this->server->getQueryInformation()->getLongQuery());
120 }else{
121 $writer->writeByteArray($this->server->getQueryInformation()->getShortQuery());
122 }
123 $interface->sendRawPacket($address, $port, $writer->getData());
124
125 return true;
126 default:
127 return false;
128 }
129 }catch(DataDecodeException $e){
130 $this->logger->debug("Bad packet from $address $port: " . $e->getMessage());
131 return false;
132 }
133 }
134}
handle(AdvancedNetworkInterface $interface, string $address, int $port, string $packet)
sendRawPacket(string $address, int $port, string $payload)