| 
<?php
/**
 * clsRcon
 * Connects with a Source dedicated server and allows you to execute rcon commands
 *
 * @author Geert Broekmans <php [at] madclog [dot] nl>
 * @copyright 2008 Geert Broekmans
 * @license GNU GPL
 * @version 1.0
 * ========================================================================
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * ========================================================================
 */
 class clsRcon {
 /**
 * Address of the server
 *
 * @var string
 */
 protected $m_sAddress;
 /**
 * Port number of the server
 *
 * @var int
 */
 protected $m_iPort;
 /**
 * rcon password
 *
 * @var string
 */
 protected $m_sPassword;
 /**
 * TCP socket for communication
 *
 * @var object
 */
 protected $m_oSocket = false;
 /**
 * rcon request id
 *
 * @var int
 */
 protected $m_iRequestId = 0;
 /**
 * timeout in usec
 *
 * @var int
 */
 protected $m_iReadTimeout = 150000;
 
 const SERVERDATA_EXECCOMMAND = 2;
 const SERVERDATA_AUTH = 3;
 const SERVERDATA_RESPONSE_VALUE = 0;
 const SERVERDATA_AUTH_RESPONSE = 2;
 
 /**
 * __construct
 * Set the variables used to connect
 *
 * @access public
 * @param string $p_sAddress
 * @param int $p_iPort
 * @param string $p_sPassword
 * @return clsRcon
 */
 public function __construct($p_sAddress, $p_iPort, $p_sPassword) {
 $this->m_sAddress = $p_sAddress;
 $this->m_iPort = $p_iPort;
 $this->m_sPassword = $p_sPassword;
 }
 
 /**
 * __destruct
 * closes the socket
 *
 * @access public
 * @return void
 */
 public function __destruct() {
 if ($this->m_oSocket !== false) {
 socket_close($this->m_oSocket);
 $this->m_oSocket = false;
 }
 }
 
 /**
 * connect
 * Connects the socket and authenticates with the server
 *
 * @access public
 * @return boolean
 */
 public function connect() {
 // create a socket
 if (($this->m_oSocket = socket_create(AF_INET,SOCK_STREAM, SOL_TCP)) === false) {
 return false;
 }
 // connect it
 if (socket_connect($this->m_oSocket, $this->m_sAddress, $this->m_iPort) === false) {
 $this->m_oSocket = false;
 return false;
 }
 // send authentication request
 $this->rawPacketSend($this->m_sPassword, null, self::SERVERDATA_AUTH);
 // read the response
 $aResult = $this->rawPacketRead();
 // check if we authenticated succesfully
 if ($aResult[0]['CommandResponse'] != self::SERVERDATA_AUTH_RESPONSE) {
 $this->__destruct();
 return false;
 } else {
 return true;
 }
 }
 
 /**
 * rcon
 * execute an rcon command
 *
 * @access public
 * @param string $p_sCommand
 * @return array
 */
 public function rcon($p_sCommand) {
 // check connection
 if($this->m_oSocket === false) {
 return false;
 }
 $this->rawPacketSend($p_sCommand);
 
 return $this->rawPacketRead();
 }
 
 /**
 * rawPacketSend
 * Builds up a packet and sends it to the server
 *
 * @access protected
 * @param string $p_sString1
 * @param string $p_sString2
 * @param int $p_iCommand
 * @return void
 */
 protected function rawPacketSend($p_sString1, $p_sString2 = NULL, $p_iCommand = self::SERVERDATA_EXECCOMMAND) {
 // build the packet backwards
 $sPacket = $p_sString1 . "\x00" . $p_sString2 . "\x00";
 // build the Request ID and Command into the Packet
 $sPacket = pack('VV',++$this->m_iRequestID, $p_iCommand) . $sPacket;
 // add the length
 $sPacket = pack('V',strlen($sPacket)) . $sPacket;
 // send the packet.
 socket_send($this->m_oSocket, $sPacket, strlen($sPacket), 0x00);
 }
 
 /**
 * rawPacketRead
 * reads and parses the rcon response
 *
 * @access protected
 * @return array
 */
 protected function rawPacketRead() {
 // the packets
 $aPackets = array();
 // our reading socket
 $aRead = array($this->m_oSocket);
 // we need to use a buffer cause sometimes a packet is send over more then 1 'read request'
 $sBuffer = '';
 while (socket_select($aRead, $aWrite = NULL, $aExcept = NULL, 0, $this->m_iReadTimeout)) {
 // get the packet length
 if (strlen($sBuffer) == 0) {
 $aPacketLength = unpack('V1PacketLength', socket_read($aRead[0], 4));
 }
 
 // read some data
 $sBuffer .= socket_read($aRead[0], $aPacketLength['PacketLength'] - strlen($sBuffer));
 // if the package is complete parse it
 if (strlen($sBuffer) == $aPacketLength['PacketLength']) {
 // read the actuall packet
 $aPacket = unpack('V1RequestID/V1CommandResponse/a*String1/a*String2', $sBuffer);
 $sBuffer = '';
 
 if (isset($aPackets[$aPacket['RequestID']]) && $aPacket['CommandResponse'] != self::SERVERDATA_AUTH_RESPONSE) {
 // existing reply, append the data
 $aPackets[$aPacket['RequestID']]['String1'] .= $aPacket['String1'];
 $aPackets[$aPacket['RequestID']]['String2'] .= $aPacket['String2'];
 } else {
 // new reply
 $aPackets[$aPacket['RequestID']] = $aPacket;
 }
 }
 }
 return array_values($aPackets);
 }
 }
 
 /**
 * test case below
 */
 //$oTest = new clsRcon('1.2.3.4', 27015, 'mypass');
 //$oTest->connect();
 //$aResponse = $oTest->rcon('cvarlist');
 //var_dump($aResponse[0]['String1']);
 
 ?>
 |