RCON : Lecture de packets sur socket UDP

Fermé
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 - Modifié le 15 août 2018 à 21:04
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 - 19 août 2018 à 02:46
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 :

    $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 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476
18 août 2018 à 15:04
tu peux aussi réutiliser, ou t'inspirer de ceci:
https://github.com/thedudeguy/PHP-Minecraft-Rcon/blob/master/src/Rcon.php
1
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
Modifié le 18 août 2018 à 16:27
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 !
0
yg_be Messages postés 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476
Modifié le 16 août 2018 à 13:25
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?
0
yg_be Messages postés 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476
Modifié le 16 août 2018 à 14:49
exemple:
do 
 {
        $buffer = fread ($this->socket, 1000);
          if ($buffer == '')
                  return 'Error';
        $result .= $buffer;
 } while ! (substr($result, - 9) == "\x20\x20\x20\x20\x20\x20\x0a\x00\x00");
0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
Modifié le 16 août 2018 à 19:28
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
0
yg_be Messages postés 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476 > Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022
18 août 2018 à 09:07
le mieux me semble alors de lire les 4 premiers octets, qui contiennent la taille du message restant. et ensuite de lire jusqu'à obtenir cette taille.
0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
Modifié le 18 août 2018 à 11:29
Salut,

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,
0
yg_be Messages postés 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476
18 août 2018 à 12:09
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 );
0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
18 août 2018 à 20:11
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)
}
0
yg_be Messages postés 22720 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 23 avril 2024 1 476 > Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022
18 août 2018 à 20:48
Beh, comment obtiens-tu "tous tes paquets"?
Peux-tu partager ton code complet, il n'est pas impossible que tu aies fait une erreur quelque part.
Utile aussi de faire
var_dump($sbuf);
, ne penses-tu pas?
0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
19 août 2018 à 00:07
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();
?>


0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
Modifié le 19 août 2018 à 00:41
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"));
?>
0
Exileur Messages postés 1475 Date d'inscription mercredi 31 août 2011 Statut Membre Dernière intervention 16 décembre 2022 150
19 août 2018 à 02:46
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"));
?>

0