socket = $socket; } /** * Close */ public function close(): void { $this->rconChallenge = ''; $this->rconPassword = ''; } /** * Open */ public function open(): void { } /** * @param string $command * * @return string * * @throws AuthenticationException * @throws InvalidPacketException */ public function command(string $command): string { if (!$this->rconChallenge) { throw new AuthenticationException('Tried to execute a RCON command before successful authorization.', AuthenticationException::BAD_PASSWORD); } $this->write(null, 'rcon ' . $this->rconChallenge . ' "' . $this->rconPassword . '" ' . $command . "\0"); $buffer = $this->read(); return $buffer->get(); } /** * @param string $password * * @throws AuthenticationException */ public function authorize(string $password): void { $this->rconPassword = $password; $this->write(null, 'challenge rcon'); $buffer = $this->socket->read(); if ($buffer->get(14) !== 'challenge rcon') { throw new AuthenticationException('Failed to get RCON challenge.', AuthenticationException::BAD_PASSWORD); } $this->rconChallenge = trim($buffer->get()); } /** * @param int|null $header * @param string $string * * @return bool */ protected function write(?int $header, string $string = ''): bool { $command = pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $string); $length = strlen($command); return $length === fwrite($this->socket->getSocket(), $command, $length); } /** * @throws AuthenticationException * @throws InvalidPacketException * * @return Buffer */ protected function read(): Buffer { // GoldSource RCON has same structure as Query $buffer = $this->socket->read(); $stringBuffer = ''; // There is no indentifier of the end, so we just need to continue reading do { $readMore = $buffer->remaining() > 0; if ($readMore) { if ($buffer->getByte() !== SourceQuery::S2A_RCON) { throw new InvalidPacketException('Invalid rcon response.', InvalidPacketException::PACKET_HEADER_MISMATCH); } $packet = $buffer->get(); $stringBuffer .= $packet; //$stringBuffer .= SubStr( $packet, 0, -2 ); // Let's assume if this packet is not long enough, there are no more after this one $readMore = strlen($packet) > 1000; // use 1300? if ($readMore) { $buffer = $this->socket->read(); } } } while ($readMore); $trimmed = trim($stringBuffer); if ($trimmed === 'Bad rcon_password.') { throw new AuthenticationException($trimmed, AuthenticationException::BAD_PASSWORD); } elseif ($trimmed === 'You have been banned from this server.') { throw new AuthenticationException($trimmed, AuthenticationException::BANNED); } $buffer->set($trimmed); return $buffer; } }