public function Command($Command) { $this->Write(SourceQuery::SERVERDATA_EXECCOMMAND, $Command); $this->Read(); $this->Buffer->GetLong(); // RequestID $Type = $this->Buffer->GetLong(); if ($Type === SourceQuery::SERVERDATA_AUTH_RESPONSE) { throw new SourceQueryException('Bad rcon_password.'); } else { if ($Type !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { return false; } } $Buffer = $this->Buffer->Get(); // We do this stupid hack to handle split packets // See https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Multiple-packet_Responses if (StrLen($Buffer) >= 4000) { do { $this->Write(SourceQuery::SERVERDATA_RESPONSE_VALUE); $this->Read(); $this->Buffer->GetLong(); // RequestID if ($this->Buffer->GetLong() !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { break; } $Buffer .= $this->Buffer->Get(); } while (false); // TODO: This is so broken that we don't even try to read multiple times, needs to be revised } // TODO: It should use GetString, but there are no null bytes at the end, why? // $Buffer = $this->Buffer->GetString( ); return $Buffer; }
public function Authorize($Password) { $this->RconPassword = $Password; switch ($this->Socket->Engine) { case CI_SourceQuery::GOLDSOURCE: $this->Write(0, 'challenge rcon'); $this->Socket->Read(); if ($this->Buffer->Get(14) != 'challenge rcon') { return false; } $this->RconChallenge = Trim($this->Buffer->Get()); break; case CI_SourceQuery::SOURCE: $this->Write(CI_SourceQuery::SERVERDATA_AUTH, $Password); $this->Read(); $RequestID = $this->Buffer->GetLong(); $Type = $this->Buffer->GetLong(); // If we receive SERVERDATA_RESPONSE_VALUE, then we need to read again // More info: https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Additional_Comments if ($Type == CI_SourceQuery::SERVERDATA_RESPONSE_VALUE) { $this->Read(); $RequestID = $this->Buffer->GetLong(); $Type = $this->Buffer->GetLong(); } if ($RequestID == -1 || $Type != CI_SourceQuery::SERVERDATA_AUTH_RESPONSE) { throw new CI_SourceQueryException('RCON authorization failed.'); } $this->RconChallenge = 1; break; } return true; }
public function Command($Command) { $this->Write(SourceQuery::SERVERDATA_EXECCOMMAND, $Command); $this->Read(); $this->Buffer->GetLong(); // RequestID $Type = $this->Buffer->GetLong(); if ($Type === SourceQuery::SERVERDATA_AUTH_RESPONSE) { throw new AuthenticationException('Bad rcon_password.', AuthenticationException::BAD_PASSWORD); } else { if ($Type !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { return false; } } $Buffer = $this->Buffer->Get(); // We do this stupid hack to handle split packets // See https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Multiple-packet_Responses if (StrLen($Buffer) >= 4000) { do { $this->Write(SourceQuery::SERVERDATA_RESPONSE_VALUE); $this->Read(); $this->Buffer->GetLong(); // RequestID if ($this->Buffer->GetLong() !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { break; } $Buffer2 = $this->Buffer->Get(); if ($Buffer2 === "") { break; } $Buffer .= $Buffer2; } while (true); } return rtrim($Buffer, ""); }
public function Read($Length = 1400) { $this->Buffer->Set(FRead($this->Socket, $Length)); if ($this->Buffer->Remaining() > 0 && $this->Buffer->GetLong() == -2) { $Packets = array(); $IsCompressed = false; $ReadMore = false; do { $RequestID = $this->Buffer->GetLong(); switch ($this->Engine) { case SourceQuery::GOLDSOURCE: $PacketCountAndNumber = $this->Buffer->GetByte(); $PacketCount = $PacketCountAndNumber & 0xf; $PacketNumber = $PacketCountAndNumber >> 4; break; case SourceQuery::SOURCE: $IsCompressed = ($RequestID & 0x80000000) != 0; $PacketCount = $this->Buffer->GetByte(); $PacketNumber = $this->Buffer->GetByte() + 1; if ($IsCompressed) { $this->Buffer->GetLong(); // Split size $PacketChecksum = $this->Buffer->GetUnsignedLong(); } else { $this->Buffer->GetShort(); // Split size } break; } $Packets[$PacketNumber] = $this->Buffer->Get(); $ReadMore = $PacketCount > sizeof($Packets); } while ($ReadMore && $this->Sherlock($Length)); $Buffer = Implode($Packets); // TODO: Test this if ($IsCompressed) { // Let's make sure this function exists, it's not included in PHP by default if (!Function_Exists('bzdecompress')) { throw new RuntimeException('Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.'); } $Data = bzdecompress($Data); if (CRC32($Data) != $PacketChecksum) { throw new SourceQueryException('CRC32 checksum mismatch of uncompressed packet data.'); } } $this->Buffer->Set(SubStr($Buffer, 4)); } }
public function Read($Length = 1400) { $this->Buffer->Set(FRead($this->Socket, $Length)); if ($this->Buffer->Remaining() === 0) { // TODO: Should we throw an exception here? return; } $Header = $this->Buffer->GetLong(); if ($Header === -1) { // We don't have to do anything } else { if ($Header === -2) { $Packets = array(); $IsCompressed = false; $ReadMore = false; do { $RequestID = $this->Buffer->GetLong(); switch ($this->Engine) { case SourceQuery::GOLDSOURCE: $PacketCountAndNumber = $this->Buffer->GetByte(); $PacketCount = $PacketCountAndNumber & 0xf; $PacketNumber = $PacketCountAndNumber >> 4; break; case SourceQuery::SOURCE: $IsCompressed = ($RequestID & 0x80000000) !== 0; $PacketCount = $this->Buffer->GetByte(); $PacketNumber = $this->Buffer->GetByte() + 1; if ($IsCompressed) { $this->Buffer->GetLong(); // Split size $PacketChecksum = $this->Buffer->GetUnsignedLong(); } else { $this->Buffer->GetShort(); // Split size } break; } $Packets[$PacketNumber] = $this->Buffer->Get(); $ReadMore = $PacketCount > sizeof($Packets); } while ($ReadMore && $this->Sherlock($Length)); $Buffer = Implode($Packets); // TODO: Test this if ($IsCompressed) { // Let's make sure this function exists, it's not included in PHP by default if (!Function_Exists('bzdecompress')) { throw new RuntimeException('Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.'); } $Buffer = bzdecompress($Buffer); if (CRC32($Buffer) !== $PacketChecksum) { throw new InvalidPacketException('CRC32 checksum mismatch of uncompressed packet data.', InvalidPacketException::CHECKSUM_MISMATCH); } } $this->Buffer->Set(SubStr($Buffer, 4)); } else { throw new InvalidPacketException('Socket read: Raw packet header mismatch. (0x' . DecHex($Header) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); } } }
public function Authorize($Password) { $this->RconPassword = $Password; $this->Write(0, 'challenge rcon'); $this->Socket->Read(); if ($this->Buffer->Get(14) !== 'challenge rcon') { return false; } $this->RconChallenge = Trim($this->Buffer->Get()); return true; }
/** * Get challenge (used for players/rules packets) * * @return bool True if all went well, false if server uses old GoldSource protocol, and it already contains answer */ private function GetChallenge($Header, $ExpectedResult) { if ($this->Challenge) { return self::GETCHALLENGE_ALL_CLEAR; } $this->Socket->Write($Header, 0xffffffff); $this->Socket->Read(); $Type = $this->Buffer->GetByte(); switch ($Type) { case self::S2A_CHALLENGE: $this->Challenge = $this->Buffer->Get(4); return self::GETCHALLENGE_ALL_CLEAR; case $ExpectedResult: // Goldsource (HLTV) return self::GETCHALLENGE_CONTAINS_ANSWER; case 0: return self::GETCHALLENGE_FAILED; default: throw new SourceQueryException('GetChallenge: Packet header mismatch. (0x' . DecHex($Type) . ')'); } }
/** * Get challenge (used for players/rules packets) * * @param $Header * @param $ExpectedResult * @throws InvalidPacketException * @return bool True if all went well, false if server uses old GoldSource protocol, and it already contains answer */ private function GetChallenge($Header, $ExpectedResult) { if ($this->Challenge) { return self::GETCHALLENGE_ALL_CLEAR; } if ($this->UseOldGetChallengeMethod) { $Header = self::A2S_SERVERQUERY_GETCHALLENGE; } $this->Socket->Write($Header, 0xffffffff); $this->Socket->Read(); $Type = $this->Buffer->GetByte(); switch ($Type) { case self::S2A_CHALLENGE: $this->Challenge = $this->Buffer->Get(4); return self::GETCHALLENGE_ALL_CLEAR; case $ExpectedResult: // Goldsource (HLTV) return self::GETCHALLENGE_CONTAINS_ANSWER; case 0: return self::GETCHALLENGE_FAILED; default: throw new InvalidPacketException('GetChallenge: Packet header mismatch. (0x' . DecHex($Type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); } }