best way to timeout a failed read()

Get help with using the PHP Secure Communications Library.

Moderator: Nuxius

Forum rules
The purpose of this forum is to provide support for phpseclib, a pure PHP SSH / SFTP / RSA library.

Posts by new users are held in a moderation queue and are not publicly visible until the post is approved.

best way to timeout a failed read()

Postby pmprojx » Thu Feb 23, 2012 6:11 pm

I've experimented with a few things but seem to be missing where the script is hanging. No doubt it's obvious but what is the best way to implement a timeout on the SSH2 read() command? I'm using it to connect to Cisco devices (so exec() is no good) for some basic diagnostics but if the prompt on the device is not what I expect then the read() hangs, endlessly searching for a match that will never come. I appreciate any help.

Thanks.
pmprojx
Traveler
 
Posts: 4
Joined: Thu Feb 23, 2012 6:00 pm

Re: best way to timeout a failed read()

Postby TerraFrost » Sun Feb 26, 2012 3:54 am

One thing you could try to do is to do stream_set_timeout($ssh->fsock, 30);
TerraFrost
Legendary Guard
 
Posts: 12217
Joined: Wed Dec 04, 2002 6:37 am

Re: best way to timeout a failed read()

Postby pmprojx » Sun Feb 26, 2012 4:31 am

Guess I should've mentioned I'd already tried using stream_set_timeout and got the same response.
pmprojx
Traveler
 
Posts: 4
Joined: Thu Feb 23, 2012 6:00 pm

Re: best way to timeout a failed read()

Postby TerraFrost » Sun Feb 26, 2012 4:36 am

Hmmm. If that doesn't work I don't know what would. I could make phpseclib utilize non-blocking sockets (eg. socket_set_blocking / socket_select with a 1 second timeout) but that'd require a rewrite of _get_binary_packet() and it'd up the version requirements for PHP.

I'll try to play around with it tomorrow.
TerraFrost
Legendary Guard
 
Posts: 12217
Joined: Wed Dec 04, 2002 6:37 am

Re: best way to timeout a failed read()

Postby TerraFrost » Tue Feb 28, 2012 6:14 am

Try this:
Code: Select all
#
#-----[ FIND ]------------------------------------------
#
    var $log_size;
#
#-----[ AFTER, ADD ]------------------------------------
#

    /**
     * Timeout
     *
     * @see Net_SSH2::Net_SSH2()
     * @access private
     */
    var $timeout;
#
#-----[ FIND ]------------------------------------------
#
        $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
#
#-----[ BEFORE, ADD ]-----------------------------------
#
        $this->timeout = $timeout;

#
#-----[ FIND ]------------------------------------------
# in _get_channel_packet()
#

            $response = $this->_get_binary_packet();
            if ($response === false) {
                user_error('Connection closed by server', E_USER_NOTICE);
                return false;
            }
#
#-----[ BEFORE, ADD ]-----------------------------------
#
            $read = array($this->fsock);
            $write = $except = NULL;

            stream_set_blocking($this->fsock, false);

            if (!stream_select($read, $write, $except, $this->timeout)) {
                $this->_close_channel($channel);
                stream_set_blocking($this->fsock, true);
                return false;
            }

            stream_set_blocking($this->fsock, true);
Not completely sure if it'll work. On Windows it gives me a "Warning: Invalid CRT parameters detected" even though I'm still able to do an exec(). Looks like it's a Windows problem:

http://bugs.php.net/bug.php?id=54563
http://bugs.php.net/bug.php?id=49948
TerraFrost
Legendary Guard
 
Posts: 12217
Joined: Wed Dec 04, 2002 6:37 am

Re: best way to timeout a failed read()

Postby pmprojx » Tue Feb 28, 2012 2:48 pm

Thanks. It's not a complete fix but I think it's close --- at least the response is different. Unfortunately, I won't have time to dig further until tomorrow. It's probably a simple tweak at this point as I can now identify the 2 points it is endlessly looping between.

The first error is triggered in _get_channel_packet().
Code: Select all
if ($response === false) {
     user_error('Connection closed by server', E_USER_NOTICE);
     return false;
}


Then it triggers this error in _get_binary_packet().
Code: Select all
if (feof($this->fsock)) {
     user_error('Connection closed prematurely', E_USER_NOTICE);
     return false;
}


It then repeats those 2 errors endlessly.


I'm using a simplified script for testing this scenario.
Code: Select all
include 'phpseclib0.2.2/Net/SSH2.php';
$ssh = new Net_SSH2('<ip_address>');
if (!$ssh->login(‘<username>’, '<password>')) {
    exit('Login Failed');
}

ob_start();

$ret = $ssh->read('<hostname>#');
echo str_replace( "\r\n", "<br />", $ret );
ob_flush(); flush();

ob_end_flush();
pmprojx
Traveler
 
Posts: 4
Joined: Thu Feb 23, 2012 6:00 pm

Re: best way to timeout a failed read()

Postby TerraFrost » Thu Mar 01, 2012 6:32 am

Here's a super hackish thing you could do:

Code: Select all
<?php
set_time_limit(2);

register_shutdown_function('shutdown');

function shutdown()
{
   set_time_limit(0);
   sleep(4);
   echo "ALL DONE!\r\n";
}

@sleep(4);
echo "WE'RE JUST GETTING STARTED\r\n";

WE'RE JUST GETTING STARTED is never output because @sleep() causes a suppressed fatal error. shutdown() is then called, the max execution time is set to unlimited and sleep() doesn't yield any fatal errors then.

I still need to look into a less hackish solution.
TerraFrost
Legendary Guard
 
Posts: 12217
Joined: Wed Dec 04, 2002 6:37 am

Re: best way to timeout a failed read()

Postby pmprojx » Thu Mar 01, 2012 3:14 pm

I tried that and got the desired response when commenting out the ssh->read line (thus timing out the connection a couple seconds after login). However, when the ssh->read command is present it still hangs. It also no longer gives the 2 error messages it was giving previously.
pmprojx
Traveler
 
Posts: 4
Joined: Thu Feb 23, 2012 6:00 pm

Re: best way to timeout a failed read()

Postby TerraFrost » Sat Mar 03, 2012 5:49 pm

The latest SVN adds support for this.

Examples:

Code: Select all
<?php
$ssh = new Net_SSH2('domain.tld');
$ssh->login('user', 'pass');
$ssh->setTimeout(10);

echo $ssh->read('[prompt]$') . "\r\n";
$ssh->write("ping 127.0.0.1\n");
echo $ssh->read('[prompt]$');

Doing an $ssh->read('[prompt]$'); after the one that timed out didn't work for me because the SSH server itself closed the connection.

Another example:

Code: Select all
<?php
$ssh = new Net_SSH2('domain.tld');
$ssh->login('user', 'pass');
$ssh->setTimeout(10);

echo $ssh->exec('ping 127.0.0.1');
echo $ssh->exec('ls -la');
TerraFrost
Legendary Guard
 
Posts: 12217
Joined: Wed Dec 04, 2002 6:37 am


Return to phpseclib support

Who is online

Users browsing this forum: No registered users and 2 guests