Telnet response does not output all data (using PHPTelnet Class)

I have a telnet request to a chassis that should be returning 500+ responses to the browser (returns 500+ when using the terminal for same request). Currently I am able to output approximately 40 of them. I have used the same code to hit different chassis and cannot output more than 60 results.

Here is my code:

<?php
require_once 'db.php';
require_once "PHPTelnet.php";

$largeParts = array();

$telnet = new PHPTelnet();
$telnet->show_connect_error=1;

$result = $telnet->Connect($server, $user, $pass);

$resultArray1 = [];
$resultArray2 = [];
$resultOutput = [];

########## bridge show all commands ##########
if ($result ==0) {
    $telnet->DoCommand('bridge show', $result);
    $resultArray1 = preg_split('/\n|\r\n?/', $result);
    //second command to include multiple page output from bridge show command
    sleep(1);
    $telnet->DoCommand('a', $result);
    $resultArray2 = preg_split('/\n|\r\n?/', $result);

    //Merge the arrays to combine all the result outputs
    $resultOutput = array_merge($resultArray1, $resultArray2);

    //duplicate cli telnet output to review
    echo '<pre>';
    echo "\n------------------------>>>>>>>\n";
    print_r($resultOutput);
    echo "\n------------------------>>>>>>>\n";
    echo '</pre>';
}

// say Disconnect(0); to break the connection without explicitly logging out
$telnet->Disconnect();


mysqli_close($mysqli);

The class for the connection is PHPTelnet (https://www.geckotribe.com/php-telnet/)

As you will see in the below code I have tried see if there are any obvious issues by using print_r for the Function GetResponse(). I have also tried to make a change in case of a stream timeout (due to the hundreds of lines that return when making the telnet call through the terminal). Here is my code for that class:

class PHPTelnet {
	var $show_connect_error=1;

	var $use_usleep=0;	// change to 1 for faster execution
		// don't change to 1 on Windows servers unless you have PHP 5
	var $sleeptime=125000;
	var $loginsleeptime=1000000;

	var $fp=NULL;
	var $loginprompt;

	var $conn1;
	var $conn2;
	
	/*
	0 = success
	1 = couldn't open network connection
	2 = unknown host
	3 = login failed
	4 = PHP version too low
	*/
	function Connect($server,$user,$pass) {
		$rv=0;
		$vers=explode('.',PHP_VERSION);
		$needvers=array(4,3,0);
		$j=count($vers);
		$k=count($needvers);
		if ($k<$j) $j=$k;
		for ($i=0;$i<$j;$i++) {
			if (($vers[$i]+0)>$needvers[$i]) break;
			if (($vers[$i]+0)<$needvers[$i]) {
				$this->ConnectError(4);
				return 4;
			}
		}
		
		$this->Disconnect();
		
		if (strlen($server)) {
			if (preg_match('/[^0-9.]/',$server)) {
				$ip=gethostbyname($server);
				if ($ip==$server) {
					$ip='';
					$rv=2;
				}
			} else $ip=$server;
		} 
		
		if (strlen($ip)) {
			if ($this->fp=fsockopen($ip,23)) {
				fputs($this->fp,$this->conn1);
				$this->Sleep();
				
				fputs($this->fp,$this->conn2);
				$this->Sleep();
				$this->GetResponse($r);
				$r=explode("\n",$r);
				$this->loginprompt=$r[count($r)-1];

				fputs($this->fp,"$user\r");
				$this->Sleep();

				fputs($this->fp,"$pass\r");
				if ($this->use_usleep) usleep($this->loginsleeptime);
				else sleep(1);
				$this->GetResponse($r);
				$r=explode("\n",$r);
				if (($r[count($r)-1]=='')||($this->loginprompt==$r[count($r)-1])) {
					$rv=3;
					$this->Disconnect();
				}
			} else $rv=1;
		}
		
		if ($rv) $this->ConnectError($rv);
		return $rv;
	}
	
	function Disconnect($exit=1) {
		if ($this->fp) {
			if ($exit) $this->DoCommand('exit',$junk);
			fclose($this->fp);
			$this->fp=NULL;
		}
	}

	function DoCommand($c,&$r) {
		if ($this->fp) {
			fputs($this->fp,"$c\r");
			$this->Sleep();
			$this->GetResponse($r);
			$r=preg_replace("/^.*?\n(.*)\n[^\n]*$/","$1",$r);
		}
		return $this->fp?1:0;
	}
	
	function GetResponse(&$r) {
		$r='';
		do { 
			// stream_set_timeout($this->fp, 60000); //This did not change anything to the response
			$r.=fread($this->fp,8000);
			##################### WHERE TO PUT THIS ? ####################################
			// added below line to test value on stream timeout for the 2 servers that have hundreds of results. Did not show any change to results.  
			//stream_set_timeout($this->fp, 60000);
			#############################################################################
			$s=socket_get_status($this->fp);

echo ">>>>>>>>>>>>>> TESTING START >>>>>>>>>>>>>> <br>";
// echo "<pre>";			
// print_r($r);
// echo "</pre>";
// echo "<br><br>";
echo "<pre>";			
print_r($s);
echo "</pre>";
echo "<br> >>>>>>>>>>>>>> TESTING END >>>>>>>>>>>>>> <br>";			

		} while ($s['unread_bytes']);

		##################### TESTING TO GET STREAM META DATA ####################################
		// $stream_meta_data = stream_get_meta_data($this->fp); //Added line
		// 	echo "<br><br><br><br>"; //Added line
		// 	echo "<pre>";
		// 	print_r($stream_meta_data); //Added line
		// 	echo "</pre>";
		#############################################################################
	}
	
	function Sleep() {
		if ($this->use_usleep) usleep($this->sleeptime);
		else sleep(1);
	}
	
	function PHPTelnet() {
		$this->conn1=chr(0xFF).chr(0xFB).chr(0x1F).chr(0xFF).chr(0xFB).
			chr(0x20).chr(0xFF).chr(0xFB).chr(0x18).chr(0xFF).chr(0xFB).
			chr(0x27).chr(0xFF).chr(0xFD).chr(0x01).chr(0xFF).chr(0xFB).
			chr(0x03).chr(0xFF).chr(0xFD).chr(0x03).chr(0xFF).chr(0xFC).
			chr(0x23).chr(0xFF).chr(0xFC).chr(0x24).chr(0xFF).chr(0xFA).
			chr(0x1F).chr(0x00).chr(0x50).chr(0x00).chr(0x18).chr(0xFF).
			chr(0xF0).chr(0xFF).chr(0xFA).chr(0x20).chr(0x00).chr(0x33).
			chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0x2C).chr(0x33).
			chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0xFF).chr(0xF0).
			chr(0xFF).chr(0xFA).chr(0x27).chr(0x00).chr(0xFF).chr(0xF0).
			chr(0xFF).chr(0xFA).chr(0x18).chr(0x00).chr(0x58).chr(0x54).
			chr(0x45).chr(0x52).chr(0x4D).chr(0xFF).chr(0xF0);
		$this->conn2=chr(0xFF).chr(0xFC).chr(0x01).chr(0xFF).chr(0xFC).
			chr(0x22).chr(0xFF).chr(0xFE).chr(0x05).chr(0xFF).chr(0xFC).chr(0x21);
	}
	
	function ConnectError($num) {
		if ($this->show_connect_error) switch ($num) {
		case 1: echo '<br />[PHP Telnet] <a href="http://www.geckotribe.com/php-telnet/errors/fsockopen.php">Connect failed: Unable to open network connection</a><br />'; break;
		case 2: echo '<br />[PHP Telnet] <a href="http://www.geckotribe.com/php-telnet/errors/unknown-host.php">Connect failed: Unknown host</a><br />'; break;
		case 3: echo '<br />[PHP Telnet] <a href="http://www.geckotribe.com/php-telnet/errors/login.php">Connect failed: Login failed</a><br />'; break;
		case 4: echo '<br />[PHP Telnet] <a href="http://www.geckotribe.com/php-telnet/errors/php-version.php">Connect failed: Your server\'s PHP version is too low for PHP Telnet</a><br />'; break;
		}
	}
}

return;

The print_r of $s from Function getResponse() outputs as follows:

Array
(
    [timed_out] => 
    [blocked] => 1
    [eof] => 
    [stream_type] => tcp_socket/ssl
    [mode] => r+
    [unread_bytes] => 0
    [seekable] => 
)

When looking to watch the telnet process real time, I used the command "-sudo watch ss-t " and it shows that the script is completing as expected and that the socket is not timing out because of a script crash. Our thought was that the socket is timing out prematurely. I tried to increase the stream_set_timeout() but to no avail as you may see from the code. I looked at the documentation https://www.php.net/manual/en/function.stream-set-timeout.php. Not sure if I am just putting that method in the wrong place or if I am missing something else… such as would the fact that the blocking is True make a difference?

which PHP version is used to execute the program.
and
does your website has an SSL?

The OP has moved on from trying to use this code, which contains a bad assumption about when the end of data has been reached, that even the php.net documentation states not to use.

php 8 and I am connecting remotely through SSL.

I am still looking at the fix for this code, along with implementing a new class GitHub - graze/telnet-client: ☎️ A telnet client written in PHP which I currently cannot connect to and am working on that also.

other assistance advised me to modify my class to read from the socket until a particular marker is seen - I am unsure of how to implement that but will make some attempts.

In the thread on the other help forum, it was pointed out that the use of the unread_bytes value isn’t the value to be testing, at all, as the loop condition in this script and that feof() should be used. However, the code you came up with using feof() didn’t use the correct variable holding the connection handle as the parameter. The correct parameter to use is $this->fp

However, as pointed out by a different member on the other forum, if the device you are reading from does not close the socket connection when finished sending the requested data, which would require experimentation to determine, feof() will not tell you when all of the data has been read and the loop using it will run forever.

In the php.net documentation for feof(), there is a specific warning about this and some code for a safe_feof() function, that will timeout if it executes longer then the current ‘default_socket_timeout’ setting, which should be set to a small, reasonable value that has meaning for the application. See this link - PHP: feof - Manual

Since your using PHP 8, your code is not modified for that. It uses PHP version 4.3.
So I suggest you must write a new class for your functions, point out the key aspects of the old script and create a new class modified for PHP 8. or if you don’t really understand OOP employ a freelancer to create that class for you.
Good luck

Thanks phdr, I had tried
while (!feof($this->fp));

I also tried adding
ini_set('default_socket_timeout', 120);

to the top of my code to test that but did not change the result - when connecting via telnet from the terminal it takes between 10sec-45sec+ depending on the server chassis I am connecting to.
As above this brings me to a 504 gateway timeout - is this due to the socket not closing and therefore once the timeout is reached then the timeout occurs?

The last line upon completing the output (identifier) is always
‘[1-4 digits number*] Bridge Interfaces displayed’
for the tasks I am running. So I am looking at where to state this as the EOF prompt in the script?

That’s an error from the web server. It could be related to the socket, but it must be debugged further to find out what is actually causing it. After about how much time does this occur? Immediately, < 1 minute, ~ 2 minutes, …?

Did you actually read the last paragraph in my reply and look at the example in the php documentation?

timeout is occurring at exactly 1min. **From tailing the log file I am also seeing that the Fatal Error is due to the Maximum execution time of 300 seconds exceeded (used php_ini to set this).

I am again looking through the php manual re: feof.

I will report back with any updates on my end.

Update - new class not connecting for me, still looking at that one.

Current class I am also trying to modify still produces a Gateway Timeout with the newly modified code addition;

//edited code to attempt to read the eof to allow all data to pass before close of socket

    function GetResponse(&$r) {

        $r = '';

        do {

             $line = fgets($this->fp);

             $r .= $line;

        } while (!preg_match('/\d{1,4} Bridge Interfaces displayed/', $line));

    }

UPDATE: Did not find a solution to the exact issue - the newer telnet class was also not connecting for me. I have, however, been able to get a perl connection using Net::Telnet component, then run a short php script to output ALL results. This happened within 3 seconds for 900+ results so all in all a good replacement.

Sponsor our Newsletter | Privacy Policy | Terms of Service