mumble-ping/mumble-ping.php

244 lines
4.5 KiB
PHP
Executable File

<?php
require "geoip2.phar";
use GeoIp2\Database\Reader;
class mumble{
public function __construct($reader_location = "GeoLite2-Country.mmdb"){
$this->reader = new Reader($reader_location);
}
public function ping($domain = "0.0.0.0", $port = 64738){
if(($ip = $this->validate_domain($domain)) === false){
throw new Exception("Server IP points to bad host");
}
$port = (int)$port;
if(!is_int($port)){
throw new Exception("Invalid port specified");
}
$this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// packet timeout
socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 2, "usec" => 0]);
socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, ["sec" => 2, "usec" => 0]);
$data = [
"version" => null,
//"ident" => null,
"online" => null,
"max" => null,
"bandwidth" => null
];
if(!@socket_connect($this->socket, $ip, $port)){
// could not connect!
socket_close($this->socket);
throw new Exception("Server is offline");
}
$payload = null;
$payload .= pack("i", 0); // request type (int)
$payload .= pack("q", 0); // identify response (long long)
socket_write($this->socket, $payload);
$data["ping"] = microtime(true);
$this->socket_recv($rawdata, 24);
$data["ping"] = round((microtime(true) - $data["ping"]) * 1000);
// separate data
$zero = true;
for($i=0; $i<4; $i++){
$str = hexdec(bin2hex($rawdata[$i]));
if(
$zero === true &&
$str != 0
){
$zero = false;
}
if($zero === false){
$data["version"] .= $str;
if($i !== 3){
$data["version"] .= ".";
}
}
}
if($data["version"] == null){
$data["version"] = "1.0.0";
}
/*
for($i=4; $i<12; $i++){
$data["ident"] .= $rawdata[$i];
}*/
for($i=12; $i<16; $i++){
$data["online"] .= $rawdata[$i];
}
for($i=16; $i<20; $i++){
$data["max"] .= $rawdata[$i];
}
for($i=20; $i<24; $i++){
$data["bandwidth"] .= $rawdata[$i];
}
/*
Get name and website
*/
$stream = fopen("list.json", "r");
$json = fread($stream, filesize("list.json"));
fclose($stream);
$json = json_decode($json, true);
$offset = -1;
for($i=0; $i<count($json); $i++){
for($k=0; $k<count($json[$i]["ip"]); $k++){
if(
(
$json[$i]["ip"][$k] == $domain ||
$json[$i]["ip"][$k] == $ip
) &&
$json[$i]["port"] === $port
){
$offset = $i;
break 2;
}
}
}
if($offset !== -1){
$data["name"] = $json[$offset]["name"];
$data["website"] = $json[$offset]["url"];
if(!preg_match('/^https?:\/\//', $data["website"])){
$data["website"] = "https://" . $data["website"];
}
$data["domains"] = $json[$offset]["ip"];
}else{
$data["name"] = null;
$data["website"] = null;
$data["domains"][] = $domain;
if($domain != $ip){
$data["domains"][] = $ip;
}
}
// get country
try{
$db = $this->reader->country($ip);
$data["countryname"] = $db->country->name;
$data["countrycode"] = $db->country->isoCode;
}catch(Exception $error){
$data["countryname"] = "Unknown";
$data["countrycode"] = "UN";
}
// unpack
$data = [
"status" => "ok",
"server" => [
"name" => $data["name"],
"domains" => $data["domains"],
"ping" => $data["ping"],
"online" => hexdec(bin2hex($data["online"])),
"max" => hexdec(bin2hex($data["max"])),
//"ident" => unpack("q", $data["ident"])[1],
"bandwidth" => (hexdec(bin2hex($data["bandwidth"])) / 1000) . " kbit/s",
"country" => [
"name" => $data["countryname"],
"code" => $data["countrycode"],
],
"version" => $data["version"],
"website" => $data["website"]
]
];
return $data;
}
private function validate_domain($url){
$ip =
str_replace(
["[", "]"], // handle ipv6
"",
$url
);
// if its not an IP
if(!filter_var($ip, FILTER_VALIDATE_IP)){
// resolve domain's IP
try{
$ip = gethostbyname($url . ".");
}catch(Exception $error){
return false;
}
}
// check if its localhost
return filter_var(
$ip,
FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);
}
private function socket_recv(&$data, $len){
error_reporting(0);
$bytes = socket_recv($this->socket, $data, $len, MSG_WAITALL);
error_reporting(1);
if(
$bytes === false || // no data
$bytes === 0 // disconnected by remote peer
){
throw new Exception("Timed out");
}
}
}
?>