-
Notifications
You must be signed in to change notification settings - Fork 62
Description
Summary
When you connect using the TS3PHPFramework via the classic "raw" way to your TeamSpeak server in the non-blocking mode, so that you can use it as bot, everything works as expected. When you simply change the port and ssh=0 to ssh=1, the PHP script starts failing within less than 5 minutes. At least in my test scenario.
Environment
- TS3PHPFramework 1.2.1 (
dev:83a2ffaace6073ca2589881a764625f2993996bc) - PHP 8.2.7
- PECL
ssh21.4
How to reproduce
- Clone this repository
- Checkout the
devbranch - Run
composer install - Add a new
test.phpscript in the root folder with the following content:
<?php
// load framework files
require_once("vendor/autoload.php");
use PlanetTeamSpeak\TeamSpeak3Framework\Adapter\ServerQuery;
use PlanetTeamSpeak\TeamSpeak3Framework\Exception\TeamSpeak3Exception;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal;
use PlanetTeamSpeak\TeamSpeak3Framework\Node\Server;
use PlanetTeamSpeak\TeamSpeak3Framework\TeamSpeak3;
class exceptionDebugging
{
private Server $ts3_VirtualServer;
function onWaitTimeout(int $idle_seconds, ServerQuery $serverquery)
{
// For debugging purposes
// Print every 30 seconds the current idle time of the bot connection.
if ($idle_seconds % 30 == 0) {
echo "No reply from the server for $idle_seconds seconds.\n";
}
// If the timestamp on the last query is more than 300 seconds (5 minutes) in the past, send 'keepalive'
// 'keepalive' command is just server query command 'clientupdate' which does nothing without properties. So nothing changes.
if ($serverquery->getQueryLastTimestamp() < time() - 260) {
echo 'Sending keep-alive.\n';
$serverquery->request('clientupdate');
}
// Get data every minute
if ($idle_seconds % 60 == 0) {
// Resetting lists
echo "Resetting lists...\n";
$this->ts3_VirtualServer->clientListReset();
$this->ts3_VirtualServer->serverGroupListReset();
// Get servergroup client info
echo "Getting client list...\n";
$this->ts3_VirtualServer->clientList(['client_type' => 0]);
echo "Getting servergroup list...\n";
$servergrouplist = $this->ts3_VirtualServer->serverGroupList(['type' => 1]);
echo "Getting servergroup client lists...\n";
$servergroup_clientlist = [];
foreach ($servergrouplist as $servergroup) {
$servergroup_clientlist[$servergroup->sgid] = count($this->ts3_VirtualServer->serverGroupClientList($servergroup->sgid));
}
// Get virtualserver info
echo "Getting info...\n";
$this->ts3_VirtualServer->getInfo(true, true);
echo "Getting connection info...\n";
$this->ts3_VirtualServer->connectionInfo();
}
}
function main()
{
echo "Connecting...\n";
try {
// Connect to the specified server, authenticate and spawn an object for the virtual server
$this->ts3_VirtualServer = TeamSpeak3::factory("serverquery://serveradmin:password@localhost:10011/?server_port=9987&ssh=0&blocking=0#no_query_clients");
} catch(TeamSpeak3Exception $e) {
// Print the error message returned by the server
die("Error " . $e->getCode() . ": " . $e->getMessage());
}
// Register for server events
echo "Register for server events...\n";
$this->ts3_VirtualServer->notifyRegister('server');
// Register a callback for serverqueryWaitTimeout events
echo "Subscribing to `serverqueryWaitTimeout`...\n";
Signal::getInstance()->subscribe('serverqueryWaitTimeout', $this->onWaitTimeout(...));
echo "Waiting for events...\n";
while (true) {
$this->ts3_VirtualServer->getAdapter()->wait();
}
}
}
$testing = new exceptionDebugging;
$testing->main();When I run it with the TCP port 10011 (classic "raw") and the option ssh=0, everything works as expected - no issues:
$ time php test.php
Connecting...
Register for server events...
Subscribing to `serverqueryWaitTimeout`...
Waiting for events...
No reply from the server for 30 seconds.
No reply from the server for 30 seconds.
No reply from the server for 60 seconds.
...
Resetting lists...
Getting client list...
Getting servergroup list...
Getting servergroup client lists...
Getting info...
Getting connection info...
No reply from the server for 30 seconds.
No reply from the server for 60 seconds.
Resetting lists...
Getting client list...
Getting servergroup list...
Getting servergroup client lists...
Getting info...
Getting connection info...
No reply from the server for 30 seconds.
real 1180m10.935s
user 0m0.311s
sys 0m0.671sAs you can see: The script was running without any issues for over 19.5 hours (1180 minutes).
However, when you change the TCP port from 10011 to 10022 (encrypted SSH) and the option from ssh=0 to ssh=1 it fails for my TeamSpeak server mostly within 5 minutes:
$ time php test.php
Connecting...
Register for server events...
Subscribing to `serverqueryWaitTimeout`...
Waiting for events...
No reply from the server for 30 seconds.
No reply from the server for 60 seconds.
Resetting lists...
Getting client list...
Getting servergroup list...
Getting servergroup client lists...
PHP Fatal error: Uncaught PlanetTeamSpeak\TeamSpeak3Framework\Exception\ServerQueryException: invalid parameter. ident 'cldbid' does not exist in node '{"_identifier":"+GPk+IrgHZUQ7Vu6aY0mGgtseNw="}' in /home/Sebbo94BY/ts3phpframework/src/Adapter/ServerQuery/Reply.php:206
Stack trace:
#0 /home/Sebbo94BY/ts3phpframework/src/Node/Server.php(1286): PlanetTeamSpeak\TeamSpeak3Framework\Adapter\ServerQuery\Reply->toAssocArray('cldbid')
#1 /home/Sebbo94BY/ts3phpframework/test.php(47): PlanetTeamSpeak\TeamSpeak3Framework\Node\Server->serverGroupClientList(147)
#2 [internal function]: exceptionDebugging->onWaitTimeout(60, Object(PlanetTeamSpeak\TeamSpeak3Framework\Adapter\ServerQuery))
#3 /home/Sebbo94BY/ts3phpframework/src/Helper/Signal/Handler.php(77): call_user_func_array(Object(Closure), Array)
#4 /home/Sebbo94BY/ts3phpframework/src/Helper/Signal.php(75): PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal\Handler->call(Array)
#5 /home/Sebbo94BY/ts3phpframework/src/Transport/Transport.php(271): PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal->emit('serverqueryWait...', Array, Object(PlanetTeamSpeak\TeamSpeak3Framework\Adapter\ServerQuery))
#6 /home/Sebbo94BY/ts3phpframework/src/Transport/TCP.php(152): PlanetTeamSpeak\TeamSpeak3Framework\Transport\Transport->waitForReadyRead()#7 /home/Sebbo94BY/ts3phpframework/src/Adapter/ServerQuery.php(177): PlanetTeamSpeak\TeamSpeak3Framework\Transport\TCP->readLine()
#8 /home/Sebbo94BY/ts3phpframework/test.php(81): PlanetTeamSpeak\TeamSpeak3Framework\Adapter\ServerQuery->wait()
#9 /home/Sebbo94BY/ts3phpframework/test.php(87): exceptionDebugging->main()
#10 {main}
thrown in /home/Sebbo94BY/ts3phpframework/src/Adapter/ServerQuery/Reply.php on line 206
real 1m8.178s
user 0m0.156s
sys 0m0.052sThis issue is always reproducible for me.
Current behaviour
The bot works as expected via the classic "raw" connection.
The bot fails with the exception invalid parameter when it tries to get any list via a SSH connection.
Expected Behaviour
The bot should be properly able to get any list via a SSH connection as it does with the classic "raw" connection.
Additionals notes
- This exception occurs for all
*List()functions of the TS3PHPFramework - Sometimes it works and returns the lists as expected, but most of the time it fails. Maybe an issue with the TCP buffer or so?
- I've already tested in a temporary written PHPUnit test, if longer (e. g. 556 clients as member of a servergroup) and especially the here returned response can be properly parsed by the TS3PHPFramework and yes, it works as expected - otherwise the classic "raw" connection would also fail. So we have two checks here. :)
- It looks like as the SSH TCP connection does not get the full response (line):
invalid parameter. ident 'cldbid' does not exist in node '{"_identifier":"+GPk+IrgHZUQ7Vu6aY0mGgtseNw="}(the node looks always a bit different, but always incomplete like this one) - If
ssh=1, we use the PHP functionsssh2_connect()andssh2_shell():ts3phpframework/src/Transport/TCP.php
Line 82 in 83a2ffa
$this->stream = @ssh2_shell($this->session, "raw"); - Sending data is done using the PHP function
fwrite():ts3phpframework/src/Transport/TCP.php
Line 182 in 83a2ffa
@fwrite($this->stream, $data); - Reading data is done using the PHP function
fgets():ts3phpframework/src/Transport/TCP.php
Line 154 in 83a2ffa
$data = @fgets($this->stream, 4096); - To be able to build a TeamSpeak bot, you need to set
blocking=0:ts3phpframework/src/Adapter/ServerQuery.php
Lines 169 to 171 in 83a2ffa
if ($this->getTransport()->getConfig("blocking")) { throw new AdapterException("only available in non-blocking mode"); } - Based on the PHP documentation of
ssh2_shell()a comment mentions, thatstream_set_blocking()should be set totrue, but we always set it tofalse:ts3phpframework/src/Transport/TCP.php
Line 90 in 83a2ffa
@stream_set_blocking($this->stream, $blocking ? 1 : 0); - Simply setting
@stream_set_blocking($this->stream, true);does unfortunately not solve the issue. Instead it stucks in a very CPU intensive endless loop, wherevar_dump($data)returnsbool(false). So it does somehow not break / exit thewhile()loop.