<?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']);
?>
|