planetteamspeak / ts3phpframework

Modern use-at-will framework that provides individual components to manage TeamSpeak 3 Server instances
https://www.planetteamspeak.com
GNU General Public License v3.0
209 stars 59 forks source link

SSH connections in non-blocking mode fail with `invalid parameter` #209

Open Sebbo94BY opened 1 year ago

Sebbo94BY commented 1 year ago

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

How to reproduce

  1. Clone this repository
  2. Checkout the dev branch
  3. Run composer install
  4. Add a new test.php script 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.671s

As 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.052s

This 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