forked from pmmp/PocketMine-MP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathping-server.php
151 lines (133 loc) · 4.67 KB
/
ping-server.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\tools\ping_server;
use pocketmine\utils\Utils;
use raklib\protocol\MessageIdentifiers;
use raklib\protocol\PacketSerializer;
use raklib\protocol\UnconnectedPing;
use raklib\protocol\UnconnectedPong;
use function bin2hex;
use function count;
use function fgets;
use function gethostbynamel;
use function hrtime;
use function intdiv;
use function mt_rand;
use function ord;
use function sleep;
use function socket_bind;
use function socket_close;
use function socket_create;
use function socket_getsockname;
use function socket_last_error;
use function socket_recvfrom;
use function socket_select;
use function socket_sendto;
use function socket_strerror;
use function strlen;
use function time;
use function trim;
use const MSG_DONTROUTE;
use const PHP_BINARY;
use const PHP_INT_MAX;
require_once 'vendor/autoload.php';
function hrtime_ms() : int{
return intdiv(hrtime(true), 1_000_000);
}
function read_stdin(string $prompt) : string{
echo $prompt . ": ";
$input = fgets(STDIN);
if($input === false){
exit(1); //this probably means the user pressed ctrl+c
}
return trim($input);
}
function ping_server(\Socket $socket, string $serverIp, int $serverPort, int $timeoutSeconds, int $rakNetClientId) : bool{
$ping = new UnconnectedPing();
$ping->sendPingTime = hrtime_ms();
$ping->clientId = $rakNetClientId;
$serializer = new PacketSerializer();
$ping->encode($serializer);
if(@socket_sendto($socket, $serializer->getBuffer(), strlen($serializer->getBuffer()), MSG_DONTROUTE, $serverIp, $serverPort) === false){
\GlobalLogger::get()->error("Failed to send ping: " . socket_strerror(socket_last_error($socket)));
return false;
}
\GlobalLogger::get()->info("Ping sent to $serverIp on port $serverPort, waiting for response (press CTRL+C to abort)");
$r = [$socket];
$w = $e = null;
if(socket_select($r, $w, $e, $timeoutSeconds) === 1){
$response = @socket_recvfrom($socket, $recvBuffer, 65535, 0, $recvAddr, $recvPort);
if($response === false){
\GlobalLogger::get()->error("Error reading from socket: " . socket_strerror(socket_last_error($socket)));
return false;
}
if($recvAddr === $serverIp && $recvPort === $serverPort && $recvBuffer !== "" && ord($recvBuffer[0]) === MessageIdentifiers::ID_UNCONNECTED_PONG){
$pong = new UnconnectedPong();
$pong->decode(new PacketSerializer($recvBuffer));
\GlobalLogger::get()->info("--- Response received ---");
\GlobalLogger::get()->info("Payload: $pong->serverName");
\GlobalLogger::get()->info("Response time: " . (hrtime_ms() - $pong->sendPingTime) . " ms");
return true;
}else{
\GlobalLogger::get()->debug("Garbage packet from $recvAddr $recvPort: " . bin2hex($recvBuffer));
}
}
\GlobalLogger::get()->info("No ping response after $timeoutSeconds seconds");
return false;
}
if(count($argv) > 3){
echo "Usage: " . PHP_BINARY . " " . __FILE__ . " [server IP] [server port]\n";
exit(1);
}
if(count($argv) > 1){
$hostName = $argv[1];
}else{
do{
$hostName = read_stdin("Server address");
}while($hostName === "");
}
$serverIps = gethostbynamel($hostName);
if($serverIps === false){
\GlobalLogger::get()->critical("Unable to resolve hostname $hostName to an IP address");
exit(1);
}
if(count($serverIps) > 1){
\GlobalLogger::get()->warning("Multiple IP addresses found for hostname $hostName, using the first one: " . $serverIps[0]);
}
$server = $serverIps[0];
\GlobalLogger::get()->info("Resolved hostname to $server");
if(count($argv) > 2){
$port = (int) $argv[2];
}elseif(count($argv) > 1){
$port = 19132;
}else{
$portRaw = read_stdin("Server port (empty for 19132)");
$port = $portRaw === "" ? 19132 : (int) $portRaw;
}
$sock = Utils::assumeNotFalse(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP));
socket_bind($sock, "0.0.0.0");
socket_getsockname($sock, $bindAddr, $bindPort);
\GlobalLogger::get()->info("Bound to $bindAddr on port $bindPort");
$start = time();
while(time() < $start + 60_000 && !ping_server($sock, $server, $port, 5, mt_rand(0, PHP_INT_MAX))){
sleep(1);
}
socket_close($sock);