PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
RCONInstance.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 
22 namespace pocketmine\network\rcon;
23 
25 
26 class RCONInstance extends \Thread{
27  public $stop;
28  public $cmd;
29  public $response;
30  private $socket;
31  private $password;
32  private $maxClients;
33 
34 
35  public function __construct($socket, $password, $maxClients = 50){
36  $this->stop = false;
37  $this->cmd = "";
38  $this->response = "";
39  $this->socket = $socket;
40  $this->password = $password;
41  $this->maxClients = (int) $maxClients;
42  for($n = 0; $n < $this->maxClients; ++$n){
43  $this->{"client" . $n} = null;
44  $this->{"status" . $n} = 0;
45  $this->{"timeout" . $n} = 0;
46  }
47 
48  $this->start();
49  }
50 
51  private function writePacket($client, $requestID, $packetType, $payload){
52  $pk = Binary::writeLInt((int) $requestID)
53  . Binary::writeLInt((int) $packetType)
54  . $payload
55  . "\x00\x00"; //Terminate payload and packet
56  return socket_write($client, Binary::writeLInt(strlen($pk)) . $pk);
57  }
58 
59  private function readPacket($client, &$size, &$requestID, &$packetType, &$payload){
60  socket_set_nonblock($client);
61  $d = socket_read($client, 4);
62  if($this->stop === true){
63  return false;
64  }elseif($d === false){
65  return null;
66  }elseif($d === "" or strlen($d) < 4){
67  return false;
68  }
69  socket_set_block($client);
70  $size = Binary::readLInt($d);
71  if($size < 0 or $size > 65535){
72  return false;
73  }
74  $requestID = Binary::readLInt(socket_read($client, 4));
75  $packetType = Binary::readLInt(socket_read($client, 4));
76  $payload = rtrim(socket_read($client, $size + 2)); //Strip two null bytes
77  return true;
78  }
79 
80  public function close(){
81  $this->stop = true;
82  }
83 
84  public function run(){
85 
86  while($this->stop !== true){
87  usleep(2000);
88  $r = [$socket = $this->socket];
89  $w = null;
90  $e = null;
91  if(socket_select($r, $w, $e, 0) === 1){
92  if(($client = socket_accept($this->socket)) !== false){
93  socket_set_block($client);
94  socket_set_option($client, SOL_SOCKET, SO_KEEPALIVE, 1);
95  $done = false;
96  for($n = 0; $n < $this->maxClients; ++$n){
97  if($this->{"client" . $n} === null){
98  $this->{"client" . $n} = $client;
99  $this->{"status" . $n} = 0;
100  $this->{"timeout" . $n} = microtime(true) + 5;
101  $done = true;
102  break;
103  }
104  }
105  if($done === false){
106  @socket_close($client);
107  }
108  }
109  }
110 
111  for($n = 0; $n < $this->maxClients; ++$n){
112  $client = &$this->{"client" . $n};
113  if($client !== null){
114  if($this->{"status" . $n} !== -1 and $this->stop !== true){
115  if($this->{"status" . $n} === 0 and $this->{"timeout" . $n} < microtime(true)){ //Timeout
116  $this->{"status" . $n} = -1;
117  continue;
118  }
119  $p = $this->readPacket($client, $size, $requestID, $packetType, $payload);
120  if($p === false){
121  $this->{"status" . $n} = -1;
122  continue;
123  }elseif($p === null){
124  continue;
125  }
126 
127  switch($packetType){
128  case 3: //Login
129  if($this->{"status" . $n} !== 0){
130  $this->{"status" . $n} = -1;
131  continue;
132  }
133  if($payload === $this->password){
134  socket_getpeername($client, $addr, $port);
135  $this->response = "[INFO] Successful Rcon connection from: /$addr:$port";
136  $this->synchronized(function (){
137  $this->wait();
138  });
139  $this->response = "";
140  $this->writePacket($client, $requestID, 2, "");
141  $this->{"status" . $n} = 1;
142  }else{
143  $this->{"status" . $n} = -1;
144  $this->writePacket($client, -1, 2, "");
145  continue;
146  }
147  break;
148  case 2: //Command
149  if($this->{"status" . $n} !== 1){
150  $this->{"status" . $n} = -1;
151  continue;
152  }
153  if(strlen($payload) > 0){
154  $this->cmd = ltrim($payload);
155  $this->synchronized(function (){
156  $this->wait();
157  });
158  $this->writePacket($client, $requestID, 0, str_replace("\n", "\r\n", trim($this->response)));
159  $this->response = "";
160  $this->cmd = "";
161  }
162  break;
163  }
164  usleep(1);
165  }else{
166  @socket_set_option($client, SOL_SOCKET, SO_LINGER, ["l_onoff" => 1, "l_linger" => 1]);
167  @socket_shutdown($client, 2);
168  @socket_set_block($client);
169  @socket_read($client, 1);
170  @socket_close($client);
171  $this->{"status" . $n} = 0;
172  $this->{"client" . $n} = null;
173  }
174  }
175  }
176  }
177  unset($this->socket, $this->cmd, $this->response, $this->stop);
178  exit(0);
179  }
180 }