Example #1
0
 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 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));
     }
 }
 /**
  * Get server information
  *
  * @throws SourceQueryException
  *
  * @return bool|array Returns array with information on success, false on failure
  */
 public function GetInfo()
 {
     if (!$this->Connected) {
         return false;
     }
     $this->Socket->Write(self::A2S_INFO, "Source Engine Query");
     $this->Socket->Read();
     $Type = $this->Buffer->GetByte();
     if ($Type == 0) {
         return false;
     }
     // Old GoldSource protocol, HLTV still uses it
     if ($Type == self::S2A_INFO_OLD && $this->Socket->Engine == self::GOLDSOURCE) {
         /**
          * If we try to read data again, and we get the result with type S2A_INFO (0x49)
          * That means this server is running dproto,
          * Because it sends answer for both protocols
          */
         $Server['Address'] = $this->Buffer->GetString();
         $Server['HostName'] = $this->Buffer->GetString();
         $Server['Map'] = $this->Buffer->GetString();
         $Server['ModDir'] = $this->Buffer->GetString();
         $Server['ModDesc'] = $this->Buffer->GetString();
         $Server['Players'] = $this->Buffer->GetByte();
         $Server['MaxPlayers'] = $this->Buffer->GetByte();
         $Server['Protocol'] = $this->Buffer->GetByte();
         $Server['Dedicated'] = Chr($this->Buffer->GetByte());
         $Server['Os'] = Chr($this->Buffer->GetByte());
         $Server['Password'] = $this->Buffer->GetByte() == 1;
         $Server['IsMod'] = $this->Buffer->GetByte() == 1;
         if ($Server['IsMod']) {
             $Mod['Url'] = $this->Buffer->GetString();
             $Mod['Download'] = $this->Buffer->GetString();
             $this->Buffer->Get(1);
             // NULL byte
             $Mod['Version'] = $this->Buffer->GetLong();
             $Mod['Size'] = $this->Buffer->GetLong();
             $Mod['ServerSide'] = $this->Buffer->GetByte() == 1;
             $Mod['CustomDLL'] = $this->Buffer->GetByte() == 1;
         }
         $Server['Secure'] = $this->Buffer->GetByte() == 1;
         $Server['Bots'] = $this->Buffer->GetByte();
         if (isset($Mod)) {
             $Server['Mod'] = $Mod;
         }
         return $Server;
     }
     if ($Type != self::S2A_INFO) {
         throw new SourceQueryException('GetInfo: Packet header mismatch. (0x' . DecHex($Type) . ')');
     }
     $Server['Protocol'] = $this->Buffer->GetByte();
     $Server['HostName'] = $this->Buffer->GetString();
     $Server['Map'] = $this->Buffer->GetString();
     $Server['ModDir'] = $this->Buffer->GetString();
     $Server['ModDesc'] = $this->Buffer->GetString();
     $Server['AppID'] = $this->Buffer->GetShort();
     $Server['Players'] = $this->Buffer->GetByte();
     $Server['MaxPlayers'] = $this->Buffer->GetByte();
     $Server['Bots'] = $this->Buffer->GetByte();
     $Server['Dedicated'] = Chr($this->Buffer->GetByte());
     $Server['Os'] = Chr($this->Buffer->GetByte());
     $Server['Password'] = $this->Buffer->GetByte() == 1;
     $Server['Secure'] = $this->Buffer->GetByte() == 1;
     // The Ship
     if ($Server['AppID'] == 2400) {
         $Server['GameMode'] = $this->Buffer->GetByte();
         $Server['WitnessCount'] = $this->Buffer->GetByte();
         $Server['WitnessTime'] = $this->Buffer->GetByte();
     }
     $Server['Version'] = $this->Buffer->GetString();
     // Extra Data Flags
     if ($this->Buffer->Remaining() > 0) {
         $Flags = $this->Buffer->GetByte();
         // The server's game port
         if ($Flags & 0x80) {
             $Server['GamePort'] = $this->Buffer->GetShort();
         }
         // The server's SteamID - does this serve any purpose?
         if ($Flags & 0x10) {
             $Server['ServerID'] = $this->Buffer->GetUnsignedLong() | $this->Buffer->GetUnsignedLong() << 32;
         }
         // The spectator port and then the spectator server name
         if ($Flags & 0x40) {
             $Server['SpecPort'] = $this->Buffer->GetShort();
             $Server['SpecName'] = $this->Buffer->GetString();
         }
         // The game tag data string for the server
         if ($Flags & 0x20) {
             $Server['GameTags'] = $this->Buffer->GetString();
         }
         // 0x01 - The server's 64-bit GameID
     }
     return $Server;
 }
Example #4
0
 /**
  * Get server information
  *
  * @throws InvalidPacketException
  *
  * @return bool|array Returns array with information on success, false on failure
  */
 public function GetInfo()
 {
     if (!$this->Connected) {
         return false;
     }
     $this->Socket->Write(self::A2S_INFO, "Source Engine Query");
     $this->Socket->Read();
     $Type = $this->Buffer->GetByte();
     if ($Type === 0) {
         return false;
     }
     // Old GoldSource protocol, HLTV still uses it
     if ($Type === self::S2A_INFO_OLD && $this->Socket->Engine === self::GOLDSOURCE) {
         /**
          * If we try to read data again, and we get the result with type S2A_INFO (0x49)
          * That means this server is running dproto,
          * Because it sends answer for both protocols
          */
         $Server['Address'] = $this->Buffer->GetString();
         $Server['HostName'] = $this->Buffer->GetString();
         $Server['Map'] = $this->Buffer->GetString();
         $Server['ModDir'] = $this->Buffer->GetString();
         $Server['ModDesc'] = $this->Buffer->GetString();
         $Server['Players'] = $this->Buffer->GetByte();
         $Server['MaxPlayers'] = $this->Buffer->GetByte();
         $Server['Protocol'] = $this->Buffer->GetByte();
         $Server['Dedicated'] = Chr($this->Buffer->GetByte());
         $Server['Os'] = Chr($this->Buffer->GetByte());
         $Server['Password'] = $this->Buffer->GetByte() === 1;
         $Server['IsMod'] = $this->Buffer->GetByte() === 1;
         if ($Server['IsMod']) {
             $Mod['Url'] = $this->Buffer->GetString();
             $Mod['Download'] = $this->Buffer->GetString();
             $this->Buffer->Get(1);
             // NULL byte
             $Mod['Version'] = $this->Buffer->GetLong();
             $Mod['Size'] = $this->Buffer->GetLong();
             $Mod['ServerSide'] = $this->Buffer->GetByte() === 1;
             $Mod['CustomDLL'] = $this->Buffer->GetByte() === 1;
         }
         $Server['Secure'] = $this->Buffer->GetByte() === 1;
         $Server['Bots'] = $this->Buffer->GetByte();
         if (isset($Mod)) {
             $Server['Mod'] = $Mod;
         }
         return $Server;
     }
     if ($Type !== self::S2A_INFO) {
         throw new InvalidPacketException('GetInfo: Packet header mismatch. (0x' . DecHex($Type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH);
     }
     $Server['Protocol'] = $this->Buffer->GetByte();
     $Server['HostName'] = $this->Buffer->GetString();
     $Server['Map'] = $this->Buffer->GetString();
     $Server['ModDir'] = $this->Buffer->GetString();
     $Server['ModDesc'] = $this->Buffer->GetString();
     $Server['AppID'] = $this->Buffer->GetShort();
     $Server['Players'] = $this->Buffer->GetByte();
     $Server['MaxPlayers'] = $this->Buffer->GetByte();
     $Server['Bots'] = $this->Buffer->GetByte();
     $Server['Dedicated'] = Chr($this->Buffer->GetByte());
     $Server['Os'] = Chr($this->Buffer->GetByte());
     $Server['Password'] = $this->Buffer->GetByte() === 1;
     $Server['Secure'] = $this->Buffer->GetByte() === 1;
     // The Ship (they violate query protocol spec by modifying the response)
     if ($Server['AppID'] === 2400) {
         $Server['GameMode'] = $this->Buffer->GetByte();
         $Server['WitnessCount'] = $this->Buffer->GetByte();
         $Server['WitnessTime'] = $this->Buffer->GetByte();
     }
     $Server['Version'] = $this->Buffer->GetString();
     // Extra Data Flags
     if ($this->Buffer->Remaining() > 0) {
         $Server['ExtraDataFlags'] = $Flags = $this->Buffer->GetByte();
         // The server's game port
         if ($Flags & 0x80) {
             $Server['GamePort'] = $this->Buffer->GetShort();
         }
         // The server's SteamID - does this serve any purpose?
         if ($Flags & 0x10) {
             $Server['ServerID'] = $this->Buffer->GetUnsignedLong() | $this->Buffer->GetUnsignedLong() << 32;
             // TODO: verify this
         }
         // The spectator port and then the spectator server name
         if ($Flags & 0x40) {
             $Server['SpecPort'] = $this->Buffer->GetShort();
             $Server['SpecName'] = $this->Buffer->GetString();
         }
         // The game tag data string for the server
         if ($Flags & 0x20) {
             $Server['GameTags'] = $this->Buffer->GetString();
         }
         // GameID -- alternative to AppID?
         if ($Flags & 0x1) {
             $Server['GameID'] = $this->Buffer->GetUnsignedLong() | $this->Buffer->GetUnsignedLong() << 32;
         }
         if ($this->Buffer->Remaining() > 0) {
             throw new InvalidPacketException('GetInfo: unread data? ' . $this->Buffer->Remaining() . ' bytes remaining in the buffer. Please report it to the library developer.', InvalidPacketException::BUFFER_NOT_EMPTY);
         }
     }
     return $Server;
 }