RCON : Lecture de packets sur socket UDP
Exileur
Messages postés
1475
Date d'inscription
Statut
Membre
Dernière intervention
-
Exileur Messages postés 1475 Date d'inscription Statut Membre Dernière intervention -
Exileur Messages postés 1475 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 !
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 !
4 réponses
yg_be
Messages postés
23541
Date d'inscription
Statut
Contributeur
Dernière intervention
Ambassadeur
1 584
tu peux aussi réutiliser, ou t'inspirer de ceci:
https://github.com/thedudeguy/PHP-Minecraft-Rcon/blob/master/src/Rcon.php
https://github.com/thedudeguy/PHP-Minecraft-Rcon/blob/master/src/Rcon.php
yg_be
Messages postés
23541
Date d'inscription
Statut
Contributeur
Dernière intervention
Ambassadeur
1 584
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?
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
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 était
En utilisant


Ducoup :
Me retourne :
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,
La methode utilisée pour l'ouverture de mon socket était
fsockopen()
En utilisant
socket_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:
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 :
Beh tout mes packet font :
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é.
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.
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 :
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")); ?>
J'avais déja vu ce dépot, puis suivi ce lien.
Seulement, la fatigue, surement, m'avais arrété à :
et les quelques trames.
Et quelque ligne plus bas, on peut lire ça :
Je vais tester !