RCON : Lecture de packets sur socket UDP
Exileur
Messages postés
1621
Date d'inscription
Statut
Membre
Dernière intervention
-
Exileur Messages postés 1621 Date d'inscription Statut Membre Dernière intervention -
Exileur Messages postés 1621 Date d'inscription Statut Membre Dernière intervention -
Salut à tous,
Voila, je galére un peu, jessaie de communiqué avec un serveur de jeu via RCON.
J'ai quelque chose de plus ou moins fonctionnelle, sauf que, je n'ai pas toute la réponse, d'un coup.
La fonction :
Le truc c'est que :
Si j'execute mon code, avec la premiere commande j'ai -> https://pastebin.com/qQhiME7D
Si j'execute mon code avec un commande differente, j'ai .. -> https://pastebin.com/zD69Apqv
Si j'execute la commande 'a' qui normalement ne retourne rien, j'ai la suite de la seconde commande qui apparait.
J'en déduit qu'un second packet est arrivé ? (malgré ma lecture des manuels des differentes commandes, ça reste super vague encore).
J'en déduit aussi que "\x20\x20\x20\x20\x20\x20\x0a\x00\x00" c'est simplement la fin de 'amxx.admin' ?
Je me plante complet ?
Une idée ?
Je peux savoir si de nouveaux
Merci d'avance de votre génie !
N'oubliez pas de marquez votre post comme résolut lorsque vous avez eu les réponses à vos questions...
Au royaume des aveugles, les borgnes sont rois.
Voila, je galére un peu, jessaie de communiqué avec un serveur de jeu via RCON.
J'ai quelque chose de plus ou moins fonctionnelle, sauf que, je n'ai pas toute la réponse, d'un coup.
La fonction :
$this->socket= fsockopen("udp://" . $this->server_ip, $this->server_port, $errno, $errstr, 2);
stream_set_timeout( $this->socket, 2);
function Communicate($command, $timeout = 1)
{
//If there is no open connection return false
if(!$this->connected)
return $this->connected;
//write command on socket
if($command != "")
fputs($this->socket, $command, strlen($command));
$result = '';
$bufferSize = 1;
do
{
$buffer = fread ($this->socket, 1);
if ($buffer == '')
return 'Error';
$result .= $buffer;
$status = socket_get_status($this->socket);
if ($status["unread_bytes"] > 0)
{
$result .= fread($this->socket, $status["unread_bytes"]);
$bufferSize = $bufferSize + $status["unread_bytes"];
}
var_dump(bin2hex($result));
} while (substr($result, - 9) == "\x20\x20\x20\x20\x20\x20\x0a\x00\x00");
return $result;
} //function Communicate($buffer)
Le truc c'est que :
Si j'execute mon code, avec la premiere commande j'ai -> https://pastebin.com/qQhiME7D
Si j'execute mon code avec un commande differente, j'ai .. -> https://pastebin.com/zD69Apqv
Si j'execute la commande 'a' qui normalement ne retourne rien, j'ai la suite de la seconde commande qui apparait.
J'en déduit qu'un second packet est arrivé ? (malgré ma lecture des manuels des differentes commandes, ça reste super vague encore).
J'en déduit aussi que "\x20\x20\x20\x20\x20\x20\x0a\x00\x00" c'est simplement la fin de 'amxx.admin' ?
Je me plante complet ?
Une idée ?
Je peux savoir si de nouveaux
$status["unread_bytes"]sont en attentes ?
Merci d'avance de votre génie !
N'oubliez pas de marquez votre post comme résolut lorsque vous avez eu les réponses à vos questions...
Au royaume des aveugles, les borgnes sont rois.
4 réponses
-
yg_be Messages postés 23437 Date d'inscription Statut Contributeur Dernière intervention Ambassadeur 1 588
tu peux aussi réutiliser, ou t'inspirer de ceci:
https://github.com/thedudeguy/PHP-Minecraft-Rcon/blob/master/src/Rcon.php-
Un génie !
J'avais déja vu ce dépot, puis suivi ce lien.
// get more info about multi-packet responses
// from the RCON protocol specification at
// https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
// currently, this script does not support multi-packet responses.
Seulement, la fatigue, surement, m'avais arrété à :
By default, SRCDS listens for RCON connections on TCP port 27015
et les quelques trames.
Et quelque ligne plus bas, on peut lire ça :
Multiple-packet Responses
Most responses are small enough to fit within the maximum possible packet size of 4096 bytes. However, a few commands such as cvarlist and, occasionally, status produce responses too long to be sent in one response packet. When this happens, the server will split the response into multiple SERVERDATA_RESPONSE_VALUE packets. Unfortunately, it can be difficult to accurately determine from the first packet alone whether the response has been split.
One common workaround is for the client to send an empty SERVERDATA_RESPONSE_VALUE packet after every SERVERDATA_EXECCOMMAND request. Rather than throwing out the erroneous request, SRCDS mirrors it back to the client, followed by another RESPONSE_VALUE packet containing 0x0000 0001 0000 0000 in the packet body field. Because SRCDS always responds to requests in the order it receives them, receiving a response packet with an empty packet body guarantees that all of the meaningful response packets have already been received. Then, the response bodies can simply be concatenated to build the full response.
Special thanks to Koraktor for discovering this trick.
Je vais tester !
-
-
yg_be Messages postés 23437 Date d'inscription Statut Contributeur Dernière intervention Ambassadeur 1 588
bonjour, le plus simple n'est-il pas de lire jusqu'à recevoir tout ce que tu attends?
ne devrais-tu pas inverser le test dans le while?-
-
Hello,
Enfaite, mon while ce base sur la donnée même et non pas la structure du packet.
J'en ai parlé au boulot vite fais, a priori, je me suis pas asser penché sur le proto.
Je viens de choper une trame (proto plus rececnt) sur le site de steam https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
Mon collegue m'a aussi conseillé de changer de methode et de plutot regarder du coté :
https://www.php.net/unpack
http://php.net/manual/fr/function.socket-create.php
https://www.php.net/manual/fr/function.socket-bind.php
https://www.php.net/manual/fr/function.socket-sendto.php
https://www.php.net/manual/fr/function.socket-select.php
https://www.php.net/manual/fr/function.socket-recv.php
Je te tiens au courant.
A plus
-
-
Salut,
La methode utilisée pour l'ouverture de mon socket étaitfsockopen()
En utilisantsocket_create ( AF_INET, SOCK_RAW, SOL_UDP )
, je peux avoir les header IP, et UDP.


Ducoup :include 'IpHeader.php'; include 'UDPHeader.php'; const PROTO_UDP = '17'; const PROTO_TCP = '6'; // Socket UDP if (($socket = @socket_create ( AF_INET, SOCK_RAW, SOL_UDP )) === FALSE) { echo "Could not create socket !"; exit ( 1 ); } socket_set_nonblock ( $socket ); echo "Starting sniffing...\n"; while ( TRUE ) { if ((socket_recv ( $socket, $raw, 65536, 0 )) > 0) { $packet = IpHeader::getHeader ( $raw ); print_r($packet); if ($packet [IpHeader::PROTO] == PROTO_UDP) { // Datagram UDP $payload = UdpHeader::getHeader ( $packet [IpHeader::PAYLOAD] ); print_r ( $payload ); } } usleep ( 100 ); }
Me retourne :Array ( [ip_ver_len] => 5 [tos] => 0 [tot_len] => 75 [identification] => 3833 [indic_frag_offset] => 16384 [ttl] => 64 [protocol] => 17 [checksum] => 11635 [src_addr] => 127.0.0.53 [dst_addr] => 127.0.0.1 [payload] => 0035a9690037fe7e75278180000100000000000012636f6e6e65637469766974792d636865636b067562756e747503636f6d00001c0001 ) Array ( [src_port] => 53 [dst_port] => 43369 [length] => 55 [checksum] => 65150 [data] => 75278180000100000000000012636f6e6e65637469766974792d636865636b067562756e747503636f6d00001c0001 )
En soit, j'ai maintenant toutes les infos qu'il me faut sur le packet. Sauf que c'est de l'UDP et que j'ai bien peur de ne pas pouvoir techniquement répondre à la problématique, je ne sais pas combien de packet vont arriver.
Et vu que ça n'a pas l'air d'étre géré dans le protocole suppérieur je suis pas sure de pouvoir faire ce que je veux.
Il va surement faloir que je m'oriente vers une boucle infinie dans un thread ou qq chose comme ça.
Je continue de chercher.
A plus,
-
tout cela est intéressant, mais inutile.
si tu lis les 4 premiers octets via fread, tu connaitras le nombre d'octets à lire ensuite.
exemple pour lire les 4 premiers octets:$result = ''; do { $buffer = fread ($this->socket, 4-strlen($result)); if ($buffer == '') return 'Error'; $result .= $buffer; } while ( len($result) <> 4 ); -
ok, comme ça je peux récupéré les 4 premier octer (donc les 32 premier bit)
Concrétement, dérière, je fais :$size = unpack("Vsize", substr($buf, 0, 4)); var_dump($size);
Beh tout mes packet font :
array(1) {
["size"]=>
int(4294967295)
}
array(1) {
["size"]=>
int(4294967295)
}
array(1) {
["size"]=>
int(4294967295)
}
array(1) {
["size"]=>
int(4294967295)
}
-
-
C'est 'un fork' de la class rcon_hl_net.php
La fonction en cause est Communicate($command)
Pour l'instant je :
- Execute la commande voulu
- Execute une commande vide
- Lit le premier packet reçu
- Check si ce packet est est une réponse ou le packet de kerator
- Check si d'autre packet sont en attente
Soit le résultat est complet,
Soit ça plante avec un "Could not receive response after 3 try: [11] Resource temporarily unavailable",
Soit le résultat est tronqué.
<?php class Rcon { var $challenge_number; var $connected; var $server_ip; var $server_password; var $server_port; var $socket; function Rcon() { $this->challenge_number = 0; $this->connected = true; $this->server_ip = ""; $this->server_port = 27015; $this->server_password = ""; } function Connect($server_ip, $server_port, $server_password = "") { $this->server_ip = gethostbyname($server_ip); $this->server_port = $server_port; $this->server_password = $server_password; $sock = socket_create(AF_INET, SOCK_DGRAM, 0); $timeout = array('sec'=>3, 'usec'=>0); socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); if(is_resource($sock)) { echo "Connected to " .$this->server_ip .PHP_EOL; $this->connected = true; } else { $this->connected = false; echo "Can't connect to " .$this->server_ip. " : " .PHP_EOL; return false; } $this->socket = $sock; return true; } function Disconnect() { socket_close($this->socket); $connected = false; } function IsConnected() { return $this->connected; } function ServerInfo() { if(!$this->connected) return $this->connected; $status = $this->RconCommand("status"); if(!$status || trim($status) == "Bad rcon_password.") return $status; $line = explode("\n", $status); $map = substr($line[3], strpos($line[3], ":") + 1); $players = trim(substr($line[4], strpos($line[4], ":") + 1)); $active = explode(" ", $players); $result["ip"] = trim(substr($line[2], strpos($line[2], ":") + 1)); $result["name"] = trim(substr($line[0], strpos($line[0], ":") + 1)); $result["map"] = trim(substr($map, 0, strpos($map, "at:"))); $result["mod"] = "Counterstrike " . trim(substr($line[1], strpos($line[1], ":") + 1)); $result["game"] = "Halflife"; $result["activeplayers"] = $active[0]; $result["maxplayers"] = substr($active[2], 1); //format player info for($i = 1; $i <= $result["activeplayers"]; $i++) { $tmp = $line[$i + 6]; if(substr_count($tmp, "#") <= 0) break; $begin = strpos($tmp, "\"") + 1; $end = strrpos($tmp, "\""); $result[$i]["name"] = substr($tmp, $begin, $end - $begin); $tmp = trim(substr($tmp, $end + 1)); $end = strpos($tmp, " "); $result[$i]["id"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["wonid"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["frag"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["time"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["ping"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $tmp = trim(substr($tmp, $end)); $result[$i]["adress"] = $tmp; } return $result; } function ServerMaps($pagenumber = 0) { if(!$this->connected) return $this->connected; $maps = $this->RconCommand("maps *", $pagenumber); if(!$maps || trim($maps) == "Bad rcon_password.") return $maps; $line = explode("\n", $maps); $count = sizeof($line) - 4; for($i = 0; $i <= $count; $i++) { $text = $line[$i]; if(strstr($text, "Dir:")) { $mapcount = 0; $directory = strstr($text, " "); } else if(strstr($text, "(fs)")) { $mappath = strstr($text, " "); if(!($tmpmap = strrchr($mappath, "/"))) $tmpmap = $mappath; $result[$directory][$i] = substr($tmpmap, 1, strpos($tmpmap, ".") - 1); } } return $result; } function Info() { if(!$this->connected) return $this->connected; $command = "\xff\xff\xff\xffTSource Engine Query\0\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC INFOS' .PHP_EOL; return false; } $buffer = explode("\x00", $buffer); $result["ip"] = substr($buffer[0], 5); $result["name"] = $buffer[1]; $result["map"] = $buffer[2]; $result["mod"] = $buffer[3]; $result["game"] = $buffer[4]; $result["activeplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][0]):"0"; $result["maxplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][1]):"0"; return $result; } function Players() { if(!$this->connected) return $this->connected; $command = "\xff\xff\xff\xffD\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC PLAYERS' .PHP_EOL; return false; } $buffer = substr($buffer, 1); for($i = 1; strlen($buffer) > 0; $i++) { $tmp = strpos($buffer, "\x00"); $result[$i]["name"] = substr($buffer, 1, $tmp); $result[$i]["frag"] = ord($buffer[$tmp + 1]) + (ord($buffer[$tmp + 2]) << 8) + (ord($buffer[$tmp + 3]) << 16) + (ord($buffer[$tmp + 4]) << 24); $tmptime = @unpack('ftime', substr($buffer, $tmp + 5, 4)); $result[$i]["time"] = date('i:s', round($tmptime['time'], 0) + 82800); $buffer = substr($buffer, $tmp + 9); } return $result; } function ServerRules() { if(!$this->connected) return $this->connected; $command = "\xff\xff\xff\xffE\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; return false; } $buffer = substr($buffer, 2); $buffer = explode("\x00", $buffer); $buffer_count = floor(sizeof($buffer) / 2); for($i = 0; $i < $buffer_count; $i++) { $result[$buffer[2 * $i]] = $buffer[2 * $i + 1]; } ksort($result); return $result; } function GetChallenge() { socket_sendto($this->socket, "\xff\xff\xff\xffchallenge rcon\n", strlen("\xff\xff\xff\xffchallenge rcon\n") , 0 , $this->server_ip , $this->server_port); socket_recvfrom($this->socket, $buffer, 4096, 0, $server, $port); if(trim($buffer) == "") { $this->connected = false; return false; } //get challenge number $buffer = explode(" ", $buffer); $this->challenge_number = trim($buffer[2]); } function RconCommand($command) { if(!$this->connected) return $this->connected; if($this->challenge_number == "") { $this->GetChallenge(); } $command = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\" $command\n"; $result = $this->Communicate($command); return trim($result); } function Communicate($command) { if(!$this->connected) return $this->connected; if($command != "") { if( ! socket_sendto($this->socket, $command , strlen($command) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send data: [$errorcode] $errormsg \n"); } $cmd_validation = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\"\n"; if( ! socket_sendto($this->socket, $cmd_validation, strlen($cmd_validation) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send validation: [$errorcode] $errormsg \n"); } } usleep(500); $result = ''; $tryNb = 0; $stayInWhile = TRUE; $packetWaiting = TRUE; do { if(socket_recvfrom($this->socket, $buf, 4096, 0, $server, $port) === FALSE) { $tryNb++; if ($tryNb >= 3 ) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not receive response after $tryNb try: [$errorcode] $errormsg \n"); } } $result .= substr($buf, 5); $toDump[] = bin2hex($buf); $read = array($this->socket); $write = NULL; $except = NULL; $packetWaiting = socket_select($read, $write, $except, 0); if (bin2hex($buf) == 'ffffffff6c0000') $stayInWhile = FALSE; if ($packetWaiting > 0) $packetWaiting = TRUE; else $packetWaiting = FALSE; usleep(300); } while ($stayInWhile || $packetWaiting ); var_dump($toDump); usleep(500); return $result; } //function Communicate($buffer) } $server = 'X.X.X.X'; $port = 27015; $M = new Rcon; $M->connect($server, $port, "Password"); print_r($M->ServerInfo()); print_r($M->RconCommand("meta")); print_r($M->RconCommand("meta cvars")); $M->disconnect(); ?>
-
Je viens de modifier l'ouverture du socket.
Comme concrétement, je ne veux pas un flux, mais seulement la réponse à ma requette, j'ouvre et ferme le flux seulement quand necessaire. Donc inséré dans Communication()
J'ai modifier la condition du while aussi.
<?php class Rcon { var $challenge_number; var $connected; var $server_ip; var $server_password; var $server_port; var $socket; function Rcon() { $this->challenge_number = 0; $this->connected = true; $this->server_ip = ""; $this->server_port = 27015; $this->server_password = ""; } function __construct($server_ip, $server_port, $server_password = "") { $this->server_ip = gethostbyname($server_ip); $this->server_port = $server_port; $this->server_password = $server_password; } function Connect() { $sock = socket_create(AF_INET, SOCK_DGRAM, 0); $timeout = array('sec'=>1, 'usec'=>0); socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); socket_set_block($sock); if(is_resource($sock)) { echo "Connected to " .$this->server_ip .PHP_EOL; $this->connected = true; } else { $this->connected = false; echo "Can't connect to " .$this->server_ip. " : " .PHP_EOL; return false; } $this->socket = $sock; return true; } function Disconnect() { socket_close($this->socket); $connected = false; } function IsConnected() { return $this->connected; } function ServerInfo() { $status = $this->RconCommand("status"); if(!$status || trim($status) == "Bad rcon_password.") return $status; $line = explode("\n", $status); $map = substr($line[3], strpos($line[3], ":") + 1); $players = trim(substr($line[4], strpos($line[4], ":") + 1)); $active = explode(" ", $players); $result["ip"] = trim(substr($line[2], strpos($line[2], ":") + 1)); $result["name"] = trim(substr($line[0], strpos($line[0], ":") + 1)); $result["map"] = trim(substr($map, 0, strpos($map, "at:"))); $result["mod"] = "Counterstrike " . trim(substr($line[1], strpos($line[1], ":") + 1)); $result["game"] = "Halflife"; $result["activeplayers"] = $active[0]; $result["maxplayers"] = substr($active[2], 1); //format player info for($i = 1; $i <= $result["activeplayers"]; $i++) { $tmp = $line[$i + 6]; if(substr_count($tmp, "#") <= 0) break; $begin = strpos($tmp, "\"") + 1; $end = strrpos($tmp, "\""); $result[$i]["name"] = substr($tmp, $begin, $end - $begin); $tmp = trim(substr($tmp, $end + 1)); $end = strpos($tmp, " "); $result[$i]["id"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["wonid"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["frag"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["time"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["ping"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $tmp = trim(substr($tmp, $end)); $result[$i]["adress"] = $tmp; } return $result; } function ServerMaps($pagenumber = 0) { $maps = $this->RconCommand("maps *", $pagenumber); if(!$maps || trim($maps) == "Bad rcon_password.") return $maps; $line = explode("\n", $maps); $count = sizeof($line) - 4; for($i = 0; $i <= $count; $i++) { $text = $line[$i]; if(strstr($text, "Dir:")) { $mapcount = 0; $directory = strstr($text, " "); } else if(strstr($text, "(fs)")) { $mappath = strstr($text, " "); if(!($tmpmap = strrchr($mappath, "/"))) $tmpmap = $mappath; $result[$directory][$i] = substr($tmpmap, 1, strpos($tmpmap, ".") - 1); } } return $result; } function Info() { $command = "\xff\xff\xff\xffTSource Engine Query\0\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC INFOS' .PHP_EOL; return false; } $buffer = explode("\x00", $buffer); $result["ip"] = substr($buffer[0], 5); $result["name"] = $buffer[1]; $result["map"] = $buffer[2]; $result["mod"] = $buffer[3]; $result["game"] = $buffer[4]; $result["activeplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][0]):"0"; $result["maxplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][1]):"0"; return $result; } function Players() { $command = "\xff\xff\xff\xffD\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC PLAYERS' .PHP_EOL; return false; } $buffer = substr($buffer, 1); for($i = 1; strlen($buffer) > 0; $i++) { $tmp = strpos($buffer, "\x00"); $result[$i]["name"] = substr($buffer, 1, $tmp); $result[$i]["frag"] = ord($buffer[$tmp + 1]) + (ord($buffer[$tmp + 2]) << 8) + (ord($buffer[$tmp + 3]) << 16) + (ord($buffer[$tmp + 4]) << 24); $tmptime = @unpack('ftime', substr($buffer, $tmp + 5, 4)); $result[$i]["time"] = date('i:s', round($tmptime['time'], 0) + 82800); $buffer = substr($buffer, $tmp + 9); } return $result; } function ServerRules() { $command = "\xff\xff\xff\xffE\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; return false; } $buffer = substr($buffer, 2); $buffer = explode("\x00", $buffer); $buffer_count = floor(sizeof($buffer) / 2); for($i = 0; $i < $buffer_count; $i++) { $result[$buffer[2 * $i]] = $buffer[2 * $i + 1]; } ksort($result); return $result; } function GetChallenge() { $this->Connect(); socket_sendto($this->socket, "\xff\xff\xff\xffchallenge rcon\n", strlen("\xff\xff\xff\xffchallenge rcon\n") , 0 , $this->server_ip , $this->server_port); socket_recvfrom($this->socket, $buffer, 4096, 0, $server, $port); $this->Disconnect(); if(trim($buffer) == "") { $this->connected = false; return false; } //get challenge number $buffer = explode(" ", $buffer); $this->challenge_number = trim($buffer[2]); } function RconCommand($command) { if($this->challenge_number == "") { $this->GetChallenge(); } $command = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\" $command\n"; $result = $this->Communicate($command); return trim($result); } function Communicate($command) { $this->Connect(); if(!$this->connected) return $this->connected; if($command != "") { if( ! socket_sendto($this->socket, $command , strlen($command) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send data: [$errorcode] $errormsg \n"); } $cmd_validation = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\"\n"; if( ! socket_sendto($this->socket, $cmd_validation, strlen($cmd_validation) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send validation: [$errorcode] $errormsg \n"); } } usleep(500); $result = ''; $tryNb = 0; $stayInWhile = TRUE; $packetWaiting = TRUE; do { if(socket_recvfrom($this->socket, $buf, 4096, 0, $server, $port) === FALSE) { $tryNb++; if ($tryNb >= 3 ) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not receive response after $tryNb try: [$errorcode] $errormsg \n"); } } $result .= substr($buf, 5); $read = array($this->socket); $write = NULL; $except = NULL; $packetWaiting = socket_select($read, $write, $except, 0); if (bin2hex($buf) == 'ffffffff6c0000') $stayInWhile = FALSE; if ($packetWaiting > 0) $packetWaiting = TRUE; else $packetWaiting = FALSE; usleep(300); } while (($stayInWhile && $packetWaiting) || $packetWaiting ); usleep(500); $this->Disconnect(); return $result; } //function Communicate($buffer) } $server = 'X.X.X.X'; $port = 27015; $M = new Rcon($server, $port, "Password"); echo "server_info" .PHP_EOL; print_r($M->ServerInfo()); echo "meta" .PHP_EOL; print_r($M->RconCommand("meta")); echo PHP_EOL. "meta_cvars" .PHP_EOL; print_r($M->RconCommand("meta cvars")); ?> -
Bon je vais au lit ..
J'ai ajouté un constructeur, joué un peu avec la condition du while.
Quitte a faire de la merde, j'ai ajouté une seconde commande vide pour généré un autre packet dont je peux être certain du contenu.
Je pense que ma connexion (trés moisi) est en cause de perte de packet ou autre. J'essaierai sur le serveur directement demain voir si ça tourne mieux.
Le code maintenant :
<?php class Rcon { var $challenge_number; var $connected; var $server_ip; var $server_password; var $server_port; var $socket; function Rcon() { $this->challenge_number = 0; $this->connected = true; $this->server_ip = ""; $this->server_port = 27015; $this->server_password = ""; } function __construct($server_ip, $server_port, $server_password = "") { $this->server_ip = gethostbyname($server_ip); $this->server_port = $server_port; $this->server_password = $server_password; } function Connect() { $sock = socket_create(AF_INET, SOCK_DGRAM, 0); $timeout = array('sec'=>2, 'usec'=>0); socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); socket_set_block($sock); if(is_resource($sock)) { echo "Connected to " .$this->server_ip .PHP_EOL; $this->connected = true; } else { $this->connected = false; echo "Can't connect to " .$this->server_ip. " : " .PHP_EOL; return false; } $this->socket = $sock; return true; } function Disconnect() { socket_close($this->socket); $this->challenge_number = ''; $this->socket = ''; $this->connected = false; } function IsConnected() { return $this->connected; } function ServerInfo() { $status = $this->RconCommand("status"); if(!$status || trim($status) == "Bad rcon_password.") return $status; $line = explode("\n", $status); $map = substr($line[3], strpos($line[3], ":") + 1); $players = trim(substr($line[4], strpos($line[4], ":") + 1)); $active = explode(" ", $players); $result["ip"] = trim(substr($line[2], strpos($line[2], ":") + 1)); $result["name"] = trim(substr($line[0], strpos($line[0], ":") + 1)); $result["map"] = trim(substr($map, 0, strpos($map, "at:"))); $result["mod"] = "Counterstrike " . trim(substr($line[1], strpos($line[1], ":") + 1)); $result["game"] = "Halflife"; $result["activeplayers"] = $active[0]; $result["maxplayers"] = substr($active[2], 1); //format player info for($i = 1; $i <= $result["activeplayers"]; $i++) { $tmp = $line[$i + 6]; if(substr_count($tmp, "#") <= 0) break; $begin = strpos($tmp, "\"") + 1; $end = strrpos($tmp, "\""); $result[$i]["name"] = substr($tmp, $begin, $end - $begin); $tmp = trim(substr($tmp, $end + 1)); $end = strpos($tmp, " "); $result[$i]["id"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["wonid"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["frag"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["time"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $end = strpos($tmp, " "); $result[$i]["ping"] = substr($tmp, 0, $end); $tmp = trim(substr($tmp, $end)); $tmp = trim(substr($tmp, $end)); $result[$i]["adress"] = $tmp; } return $result; } function ServerMaps($pagenumber = 0) { $maps = $this->RconCommand("maps *", $pagenumber); if(!$maps || trim($maps) == "Bad rcon_password.") return $maps; $line = explode("\n", $maps); $count = sizeof($line) - 4; for($i = 0; $i <= $count; $i++) { $text = $line[$i]; if(strstr($text, "Dir:")) { $mapcount = 0; $directory = strstr($text, " "); } else if(strstr($text, "(fs)")) { $mappath = strstr($text, " "); if(!($tmpmap = strrchr($mappath, "/"))) $tmpmap = $mappath; $result[$directory][$i] = substr($tmpmap, 1, strpos($tmpmap, ".") - 1); } } return $result; } function Info() { $command = "\xff\xff\xff\xffTSource Engine Query\0\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC INFOS' .PHP_EOL; return false; } $buffer = explode("\x00", $buffer); $result["ip"] = substr($buffer[0], 5); $result["name"] = $buffer[1]; $result["map"] = $buffer[2]; $result["mod"] = $buffer[3]; $result["game"] = $buffer[4]; $result["activeplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][0]):"0"; $result["maxplayers"] = (strlen($buffer[5]) > 1)?ord($buffer[5][1]):"0"; return $result; } function Players() { $command = "\xff\xff\xff\xffD\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; echo 'DC PLAYERS' .PHP_EOL; return false; } $buffer = substr($buffer, 1); for($i = 1; strlen($buffer) > 0; $i++) { $tmp = strpos($buffer, "\x00"); $result[$i]["name"] = substr($buffer, 1, $tmp); $result[$i]["frag"] = ord($buffer[$tmp + 1]) + (ord($buffer[$tmp + 2]) << 8) + (ord($buffer[$tmp + 3]) << 16) + (ord($buffer[$tmp + 4]) << 24); $tmptime = @unpack('ftime', substr($buffer, $tmp + 5, 4)); $result[$i]["time"] = date('i:s', round($tmptime['time'], 0) + 82800); $buffer = substr($buffer, $tmp + 9); } return $result; } function ServerRules() { $command = "\xff\xff\xff\xffE\x00"; $buffer = $this->Communicate($command); if(trim($buffer) == "") { $this->connected = false; return false; } $buffer = substr($buffer, 2); $buffer = explode("\x00", $buffer); $buffer_count = floor(sizeof($buffer) / 2); for($i = 0; $i < $buffer_count; $i++) { $result[$buffer[2 * $i]] = $buffer[2 * $i + 1]; } ksort($result); return $result; } function GetChallenge() { $this->Connect(); socket_sendto($this->socket, "\xff\xff\xff\xffchallenge rcon\n", strlen("\xff\xff\xff\xffchallenge rcon\n") , 0 , $this->server_ip , $this->server_port); socket_recvfrom($this->socket, $buffer, 4096, 0, $server, $port); $this->Disconnect(); if(trim($buffer) == "") { $this->connected = false; return false; } //get challenge number $buffer = explode(" ", $buffer); $this->challenge_number = trim($buffer[2]); } function RconCommand($command) { if($this->challenge_number == "") { $this->GetChallenge(); } $command = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\" $command\n"; $result = $this->Communicate($command); return trim($result); } function Communicate($command) { $this->Connect(); if(!$this->connected) return $this->connected; if($command != "") { if( ! socket_sendto($this->socket, $command , strlen($command) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send data: [$errorcode] $errormsg \n"); } usleep(200); $cmd_validation = "\xff\xff\xff\xffrcon $this->challenge_number \"$this->server_password\"\n"; if( ! socket_sendto($this->socket, $cmd_validation, strlen($cmd_validation) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not send validation: [$errorcode] $errormsg \n"); } } usleep(500); $result = ''; $tryNb = 0; $stayInWhile = TRUE; $packetWaiting = TRUE; do { sleep(1); if(socket_recvfrom($this->socket, $buf, 4096, 0, $server, $port) === FALSE) { $tryNb++; $stayInWhile = FALSE; if ($tryNb == 3 ) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die("Could not receive response after $tryNb try: [$errorcode] $errormsg \n"); } } $read = array($this->socket); $write = NULL; $except = NULL; if (bin2hex($buf) == 'ffffffff6c0000') { $packetWaiting = socket_select($read, $write, $except, 0); if($stayInWhile) { if( ! socket_sendto($this->socket, $cmd_validation, strlen($cmd_validation) , 0 , $this->server_ip , $this->server_port)) { $errorcode = socket_last_error(); $errormsg = socket_strerror($errorcode); die ("Could not send revalidation: [$errorcode] $errormsg \n"); } $packetWaiting = 1; } $stayInWhile = FALSE; } else { $result .= substr($buf, 5); $packetWaiting = socket_select($read, $write, $except, 0); } if ($packetWaiting > 0) { $packetWaiting = TRUE; } else { $packetWaiting = FALSE; } usleep(30000); print_r("pkt wait : ".$packetWaiting.PHP_EOL); print_r("stay in : ".$stayInWhile.PHP_EOL); } while (( $stayInWhile/* && $packetWaiting*/) || $packetWaiting ); $this->Disconnect(); return $result; } //function Communicate($buffer) } $server = 'X.X.X.X'; $port = 27015; $M = new Rcon($server, $port, "Password"); echo "server_info" .PHP_EOL; print_r($M->ServerInfo()); echo "meta" .PHP_EOL; print_r($M->RconCommand("meta")); echo PHP_EOL. "meta_cvars" .PHP_EOL; print_r($M->RconCommand("meta cvars")); ?>
-