diff --git a/app/Controller/Component/ServerComponent.php b/app/Controller/Component/ServerComponent.php index 3ee0db65..84c5aff1 100755 --- a/app/Controller/Component/ServerComponent.php +++ b/app/Controller/Component/ServerComponent.php @@ -85,7 +85,7 @@ public function call($methods = [], $server_id = false, $debug = false) $multi = false; } - if ($config['type'] == 1 || $config['type'] == 2) { + if ($config['type'] == 1 || $config['type'] == 2 || $config['type'] == 3) { $methodsName = array_map(function ($method) { return array_keys($method)[0]; }, $methods); @@ -100,7 +100,7 @@ public function call($methods = [], $server_id = false, $debug = false) } if (count($methodsName) > 0) { - $ping = $this->ping(['ip' => $config['ip'], 'port' => $config['port']]); + $ping = $this->ping(['ip' => $config['ip'], 'port' => $config['port'], 'udp' => $config['type'] == 3]); foreach ($methods as $key => $method) { $name = array_keys($method)[0]; if (isset($ping[$name])) @@ -216,7 +216,7 @@ public function ping($config = false) App::import('Vendor', 'MinecraftPingException', ['file' => 'ping-xpaw/MinecraftPingException.php']); try { - $Query = new MinecraftPing($config['ip'], $config['port'], $this->getTimeout()); + $Query = new MinecraftPing($config['ip'], $config['port'], $this->getTimeout(), $config['udp']); $Info = $Query->Query(); } catch (MinecraftPingException $e) { return false; @@ -369,10 +369,8 @@ public function online($server_id = false, $debug = false) $config = $this->getConfig($server_id); if (!$config) // server not found return $this->online[$server_id] = false; - - if ($config['type'] == 1 || $config['type'] == 2) // ping only - return $this->online[$server_id] = ($this->ping(['ip' => $config['ip'], 'port' => $config['port']])) ? true : false; - + if ($config['type'] == 1 || $config['type'] == 2 || $config['type'] == 3) // ping only + return $this->online[$server_id] = ($this->ping(['ip' => $config['ip'], 'port' => $config['port'], 'udp' => $config['type'] == 3])) ? true : false; list($return, $code, $error) = $this->request($this->getUrl($server_id), $this->encryptWithKey("[]")); if ($return && $code === 200) return $this->online[$server_id] = true; diff --git a/app/Controller/ServerController.php b/app/Controller/ServerController.php index 06f7490b..8b4d0ba7 100755 --- a/app/Controller/ServerController.php +++ b/app/Controller/ServerController.php @@ -263,6 +263,7 @@ public function admin_link_ajax() * 0 : Plugin * 1 : Ping * 2 : Rcon + * 3 : Ping MCPE */ if ($this->request->data['type'] == 0) { @@ -277,8 +278,8 @@ public function admin_link_ajax() } } // use simple ping to retrieve data from MC protocol - else if ($this->request->data['type'] == 1) { - if (!$this->Server->ping(['ip' => $this->request->data['host'], 'port' => $this->request->data['port']])) { + else if ($this->request->data['type'] == 1 || $this->request->data['type'] == 3) { + if (!$this->Server->ping(['ip' => $this->request->data['host'], 'port' => $this->request->data['port'], 'udp' => $this->request->data['type'] == 3])) { $msg = $this->Lang->get('SERVER__LINK_ERROR_FAILED'); $msg .= $this->linkDebugPing(); return $this->response->body(json_encode(['statut' => false, 'msg' => $msg])); @@ -318,37 +319,6 @@ public function admin_link_ajax() return $this->response->body(json_encode(['statut' => true, 'msg' => $this->Lang->get('SERVER__LINK_SUCCESS')])); } - private function linkDebugFull($msg, $host, $port) - { - $msg .= $this->linkDebugPing(); - - $msg .= "

"; - $msg .= " "; - - if ($this->Server->ping(['ip' => $host, 'port' => $port])) - $msg .= $this->Lang->get('SERVER__SEEMS_USED'); - else - $msg .= $this->Lang->get('SERVER__PORT_CLOSE_OR_BAD'); - - return $msg; - } - - private function linkDebugPing() - { - $msg = "

"; - - $hypixelIp = gethostbyname('mc.hypixel.net'); - if ($this->Server->ping(['ip' => $hypixelIp, 'port' => 25565])) { - $msg .= " "; - $msg .= $this->Lang->get('SERVER__PORT_OPEN'); - } else { - $msg .= " "; - $msg .= $this->Lang->get('SERVER__SEEMS_CLOSE_OR_BLOCKED'); - } - - return $msg; - } - public function admin_banlist($server_id = false) { $this->layout = 'admin'; @@ -406,4 +376,35 @@ public function admin_online($server_id = false) $this->set('title_for_layout', $this->Lang->get('SERVER__STATUS_ONLINE')); } + private function linkDebugFull($msg, $host, $port, $udp = false) + { + $msg .= $this->linkDebugPing(); + + $msg .= "

"; + $msg .= " "; + + if ($this->Server->ping(['ip' => $host, 'port' => $port, 'udp' => $udp])) + $msg .= $this->Lang->get('SERVER__SEEMS_USED'); + else + $msg .= $this->Lang->get('SERVER__PORT_CLOSE_OR_BAD'); + + return $msg; + } + + private function linkDebugPing() + { + $msg = "

"; + + $hypixelIp = gethostbyname('mc.hypixel.net'); + if ($this->Server->ping(['ip' => $hypixelIp, 'port' => 25565, 'udp' => false])) { + $msg .= " "; + $msg .= $this->Lang->get('SERVER__PORT_OPEN'); + } else { + $msg .= " "; + $msg .= $this->Lang->get('SERVER__SEEMS_CLOSE_OR_BLOCKED'); + } + + return $msg; + } + } diff --git a/app/View/Admin/admin_index.ctp b/app/View/Admin/admin_index.ctp index 947e2c5c..dcf56e54 100755 --- a/app/View/Admin/admin_index.ctp +++ b/app/View/Admin/admin_index.ctp @@ -272,7 +272,7 @@
online($value['Server']['id'])) { ?> - can('SEND_SERVER_COMMAND_FROM_DASHBOARD')) { ?> + can('SEND_SERVER_COMMAND_FROM_DASHBOARD')) { ?>
@@ -179,7 +180,7 @@ var infos = '
get('SERVER__TYPE_DEFAULT_INFOS')) ?>
' select.parent().parent().find('input[name="server_data[rcon_port]"]').parent().remove() select.parent().parent().find('input[name="server_data[rcon_password]"]').parent().remove() - } else if (type == 1) { + } else if (type == 1 || type == 3) { var infos = '
get('SERVER__TYPE_QUERY_INFOS')) ?>
' select.parent().parent().find('input[name="server_data[rcon_port]"]').parent().remove() select.parent().parent().find('input[name="server_data[rcon_password]"]').parent().remove() diff --git a/lang/en_UK.json b/lang/en_UK.json index 1d9d82b7..1db98be2 100755 --- a/lang/en_UK.json +++ b/lang/en_UK.json @@ -533,6 +533,7 @@ "SERVER__TYPE": "Type of connection", "SERVER__TYPE_DEFAULT": "Default", "SERVER__TYPE_QUERY": "Ping (no need for plugin)", + "SERVER__TYPE_QUERY_MCPE": "Ping MCPE (no need for plugin)", "SERVER__TYPE_RCON": "RCON (no need for plugin)", "SERVER__TYPE_DEFAULT_INFOS": "You need to install the MineWebBridge plugin on your server to use this type of link.All information related to this manipulation is available on documentation ", "SERVER__TYPE_QUERY_INFOS": "You do not need to install the MineWebBridge plugin on your server to use this type of link, this type of link only allows you to display the number of connected on your infrastructure. this manipulation are available on documentation ", diff --git a/lang/en_US.json b/lang/en_US.json index 82165a38..1464e086 100755 --- a/lang/en_US.json +++ b/lang/en_US.json @@ -537,6 +537,7 @@ "SERVER__TYPE": "Type of connection", "SERVER__TYPE_DEFAULT": "Default", "SERVER__TYPE_QUERY": "Ping (no need for plugin)", + "SERVER__TYPE_QUERY_MCPE": "Ping MCPE (no need for plugin)", "SERVER__TYPE_RCON": "RCON (no need for plugin)", "SERVER__TYPE_DEFAULT_INFOS": "You need to install the MineWebBridge plugin on your server to use this type of link.All information related to this manipulation is available on documentation ", "SERVER__TYPE_QUERY_INFOS": "You do not need to install the MineWebBridge plugin on your server to use this type of link, this type of link only allows you to display the number of connected on your infrastructure. this manipulation are available on documentation ", diff --git a/lang/fr_FR.json b/lang/fr_FR.json index 28e3121d..abf8f99e 100755 --- a/lang/fr_FR.json +++ b/lang/fr_FR.json @@ -535,6 +535,7 @@ "SERVER__TYPE": "Type de la connexion", "SERVER__TYPE_DEFAULT": "Par défaut", "SERVER__TYPE_QUERY": "Ping (pas besoin de plugin)", + "SERVER__TYPE_QUERY_MCPE": "Ping MCPE (pas besoin de plugin)", "SERVER__TYPE_RCON": "RCON (pas besoin de plugin)", "SERVER__TYPE_DEFAULT_INFOS": "Vous avez besoin d'installer le plugin MineWebBridge sur votre serveur pour utiliser ce type de liaison. Toutes les informations relatives à cette manipulation sont disponibles sur la documentation", "SERVER__TYPE_QUERY_INFOS": "Vous n'avez pas besoin d'installer le plugin MineWebBridge sur votre serveur pour utiliser ce type de liaison. Ce type de liaison vous permet seulement d'afficher le nombre de connecté sur votre infrastructure. Toutes les informations relatives à cette manipulation sont disponibles sur la documentation", diff --git a/lang/ru_RU.json b/lang/ru_RU.json index e40f2214..23ed1233 100644 --- a/lang/ru_RU.json +++ b/lang/ru_RU.json @@ -537,6 +537,7 @@ "SERVER__TYPE": "Тип подключения", "SERVER__TYPE_DEFAULT": "По умолчанию", "SERVER__TYPE_QUERY": "Ping (не нужен плагин)", + "SERVER__TYPE_QUERY_MCPE": "Ping MCPE (не нужен плагин)", "SERVER__TYPE_RCON": "RCON (не требуется плагин)", "SERVER__TYPE_DEFAULT_INFOS": "Вам необходимо установить плагин MineWebBridge на свой сервер, чтобы использовать этот тип ссылки. Вся информация, касающаяся этой манипуляции, доступна на документация ", "SERVER__TYPE_QUERY_INFOS": "Вам не нужно устанавливать плагин MineWebBridge на вашем сервере, чтобы использовать ссылку этого типа, этот тип ссылки позволяет только отображать количество подключенных в вашей инфраструктуре. Эти манипуляции доступны на документация ", diff --git a/vendors/ping-xpaw/MinecraftPing.php b/vendors/ping-xpaw/MinecraftPing.php index a06afa8c..97d0fda6 100755 --- a/vendors/ping-xpaw/MinecraftPing.php +++ b/vendors/ping-xpaw/MinecraftPing.php @@ -25,15 +25,19 @@ class MinecraftPing */ private $Socket; + private $UDP = false; private $ServerAddress; private $ServerPort; private $Timeout; - public function __construct($Address, $Port = 25565, $Timeout = 2, $ResolveSRV = true) + const RAKNET_MAGIC = "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"; + + public function __construct($Address, $Port = 25565, $Timeout = 2, $udp = false, $ResolveSRV = true) { $this->ServerAddress = $Address; $this->ServerPort = (int)$Port; $this->Timeout = (int)$Timeout; + $this->UDP = (bool)$udp; if ($ResolveSRV) { $this->ResolveSRV(); @@ -42,116 +46,153 @@ public function __construct($Address, $Port = 25565, $Timeout = 2, $ResolveSRV = $this->Connect(); } - private function ResolveSRV() + public function __destruct() { - if (ip2long($this->ServerAddress) !== false) { - return; - } - - $Record = @dns_get_record('_minecraft._tcp.' . $this->ServerAddress, DNS_SRV); - - if (empty($Record)) { - return; - } + $this->Close(); + } - if (isset($Record[0]['target'])) { - $this->ServerAddress = $Record[0]['target']; - } + public function Close() + { + if ($this->Socket !== null) { + fclose($this->Socket); - if (isset($Record[0]['port'])) { - $this->ServerPort = $Record[0]['port']; + $this->Socket = null; } } public function Connect() { $connectTimeout = $this->Timeout; - $this->Socket = @fsockopen($this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout); + if ($this->UDP) + $this->Socket = @fsockopen("udp://" . $this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout); + else + $this->Socket = @fsockopen($this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout); if (!$this->Socket) { $this->Socket = null; - throw new MinecraftPingException("Failed to connect or create a socket: $errno ($errstr)"); - } + } // Set Read/Write timeout - stream_set_timeout($this->Socket, $this->Timeout); + stream_set_timeout($this->Socket, $connectTimeout); } - public function __destruct() + public function Query() { - $this->Close(); - } + if ($this->UDP) { + stream_set_blocking($this->Socket, true); + $r1 = mt_rand(PHP_INT_MIN, PHP_INT_MAX); + $r2 = mt_rand(PHP_INT_MIN, PHP_INT_MAX); + $send = "\x01" . pack('J', $r1) . self::RAKNET_MAGIC . pack('J', $r2); + stream_socket_sendto($this->Socket, $send); - public function Close() - { - if ($this->Socket !== null) { - fclose($this->Socket); + $buf = stream_socket_recvfrom($this->Socket, 2048); - $this->Socket = null; - } - } + if (count($buf) < 1 || $buf[0] != "\x1c") { + throw new MinecraftPingException("Unable to get a ping response."); + } + $ping_raw = substr($buf, strlen(self::RAKNET_MAGIC) + 19); + $ping_datas = explode(';', $ping_raw); - public function Query() - { - $TimeStart = microtime(true); // for read timeout purposes + $Data['description'] = $ping_datas[1]; + $Data['version']['name'] = $ping_datas[0]; + $Data['players']['online'] = $ping_datas[4]; + $Data['players']['max'] = $ping_datas[5]; + } else { + $TimeStart = microtime(true); // for read timeout purposes - // See http://wiki.vg/Protocol (Status Ping) - $Data = "\x00"; // packet ID = 0 (varint) + // See http://wiki.vg/Protocol (Status Ping) + $Data = "\x00"; // packet ID = 0 (varint) - $Data .= "\x04"; // Protocol version (varint) - $Data .= Pack('c', StrLen($this->ServerAddress)) . $this->ServerAddress; // Server (varint len + UTF-8 addr) - $Data .= Pack('n', $this->ServerPort); // Server port (unsigned short) - $Data .= "\x01"; // Next state: status (varint) + $Data .= "\x04"; // Protocol version (varint) + $Data .= Pack('c', StrLen($this->ServerAddress)) . $this->ServerAddress; // Server (varint len + UTF-8 addr) + $Data .= Pack('n', $this->ServerPort); // Server port (unsigned short) + $Data .= "\x01"; // Next state: status (varint) - $Data = Pack('c', StrLen($Data)) . $Data; // prepend length of packet ID + data + $Data = Pack('c', StrLen($Data)) . $Data; // prepend length of packet ID + data - fwrite($this->Socket, $Data); // handshake - fwrite($this->Socket, "\x01\x00"); // status ping + fwrite($this->Socket, $Data); // handshake + fwrite($this->Socket, "\x01\x00"); // status ping - $Length = $this->ReadVarInt(); // full packet length + $Length = $this->ReadVarInt(); // full packet length - if ($Length < 10) { - return false; - } + if ($Length < 10) { + return false; + } + + $this->ReadVarInt(); // packet type, in server ping it's 0 + + $Length = $this->ReadVarInt(); // string length - $this->ReadVarInt(); // packet type, in server ping it's 0 + $Data = ""; + do { + if (microtime(true) - $TimeStart > $this->Timeout) { + throw new MinecraftPingException('Server read timed out'); + } - $Length = $this->ReadVarInt(); // string length + $Remainder = $Length - StrLen($Data); + $block = fread($this->Socket, $Remainder); // and finally the json string + // abort if there is no progress + if (!$block) { + throw new MinecraftPingException('Server returned too few data'); + } - $Data = ""; - do { - if (microtime(true) - $TimeStart > $this->Timeout) { - throw new MinecraftPingException('Server read timed out'); + $Data .= $block; + } while (StrLen($Data) < $Length); + + if ($Data === false) { + throw new MinecraftPingException('Server didn\'t return any data'); } - $Remainder = $Length - StrLen($Data); - $block = fread($this->Socket, $Remainder); // and finally the json string - // abort if there is no progress - if (!$block) { - throw new MinecraftPingException('Server returned too few data'); + $Data = JSON_Decode($Data, true); + + if (JSON_Last_Error() !== JSON_ERROR_NONE) { + if (Function_Exists('json_last_error_msg')) { + throw new MinecraftPingException(JSON_Last_Error_Msg()); + } else { + throw new MinecraftPingException('JSON parsing failed'); + } + return false; } + } + return $Data; + } - $Data .= $block; - } while (StrLen($Data) < $Length); + public function QueryOldPre17() + { + fwrite($this->Socket, "\xFE\x01"); + $Data = fread($this->Socket, 512); + $Len = StrLen($Data); - if ($Data === false) { - throw new MinecraftPingException('Server didn\'t return any data'); + if ($Len < 4 || $Data[0] !== "\xFF") { + return false; } - $Data = JSON_Decode($Data, true); + $Data = SubStr($Data, 3); // Strip packet header (kick message packet and short length) + $Data = iconv('UTF-16BE', 'UTF-8', $Data); - if (JSON_Last_Error() !== JSON_ERROR_NONE) { - if (Function_Exists('json_last_error_msg')) { - throw new MinecraftPingException(JSON_Last_Error_Msg()); - } else { - throw new MinecraftPingException('JSON parsing failed'); - } + // Are we dealing with Minecraft 1.4+ server? + if ($Data[1] === "\xA7" && $Data[2] === "\x31") { + $Data = Explode("\x00", $Data); - return false; + return [ + 'HostName' => $Data[3], + 'Players' => IntVal($Data[4]), + 'MaxPlayers' => IntVal($Data[5]), + 'Protocol' => IntVal($Data[1]), + 'Version' => $Data[2] + ]; } - return $Data; + $Data = Explode("\xA7", $Data); + + return [ + 'HostName' => SubStr($Data[0], 0, -1), + 'Players' => isset($Data[1]) ? IntVal($Data[1]) : 0, + 'MaxPlayers' => isset($Data[2]) ? IntVal($Data[2]) : 0, + 'Protocol' => 0, + 'Version' => '1.3' + ]; } private function ReadVarInt() @@ -182,40 +223,24 @@ private function ReadVarInt() return $i; } - public function QueryOldPre17() + private function ResolveSRV() { - fwrite($this->Socket, "\xFE\x01"); - $Data = fread($this->Socket, 512); - $Len = StrLen($Data); - - if ($Len < 4 || $Data[0] !== "\xFF") { - return false; + if (ip2long($this->ServerAddress) !== false) { + return; } - $Data = SubStr($Data, 3); // Strip packet header (kick message packet and short length) - $Data = iconv('UTF-16BE', 'UTF-8', $Data); - - // Are we dealing with Minecraft 1.4+ server? - if ($Data[1] === "\xA7" && $Data[2] === "\x31") { - $Data = Explode("\x00", $Data); + $Record = @dns_get_record('_minecraft._tcp.' . $this->ServerAddress, DNS_SRV); - return [ - 'HostName' => $Data[3], - 'Players' => IntVal($Data[4]), - 'MaxPlayers' => IntVal($Data[5]), - 'Protocol' => IntVal($Data[1]), - 'Version' => $Data[2] - ]; + if (empty($Record)) { + return; } - $Data = Explode("\xA7", $Data); + if (isset($Record[0]['target'])) { + $this->ServerAddress = $Record[0]['target']; + } - return [ - 'HostName' => SubStr($Data[0], 0, -1), - 'Players' => isset($Data[1]) ? IntVal($Data[1]) : 0, - 'MaxPlayers' => isset($Data[2]) ? IntVal($Data[2]) : 0, - 'Protocol' => 0, - 'Version' => '1.3' - ]; + if (isset($Record[0]['port'])) { + $this->ServerPort = $Record[0]['port']; + } } }