PHP ssh2_connect() Implement A Timeout
Asked Answered
S

4

8

I am using the PHP ssh2 library, and simply doing:

$ssh = ssh2_connect($hostname, $port);

The problem is I want to set a timeout, i.e. after 5 seconds stop trying to connect. As far as I can tell the ssh2 library does not support a timeout on connect natively. How can I implement a timeout wrapper?

Songer answered 24/5, 2012 at 3:0 Comment(1)
did you ever figure out how to do this?Crosseyed
N
8

I know this is an old thread, but the problem is still there. So, here is the solution for it.

ssh2_connect() uses socket_connect(). socket_connect relies on the php ini configuration parameter default_socket_timeout which is set by default to 60 seconds (http://php.net/manual/en/filesystem.configuration.php#ini.default-socket-timeout)

So, the easiest way to solve our problem is to change the ini setting at runtime to a value we want, and than back to the value set in the ini file so we avoid effecting other parts of our software. In the example below, the new values is set to 2 seconds.

    $originalConnectionTimeout = ini_get('default_socket_timeout');
    ini_set('default_socket_timeout', 2);

    $connection = ssh2_connect('1.1.1.1');

    ini_set('default_socket_timeout', $originalConnectionTimeout);

You can find further details about how ssh2 for php works by reading the source code of libssh2 (https://github.com/libssh2/libssh2).

Nonjuror answered 14/9, 2017 at 8:54 Comment(0)
P
2

I've been struggling with the same problem for a while. The fact is that trying to connect to a "dead" or unresponsive server with ssh2 will stall your application for as long as the target server's max connection time limit.

 

The easy way to detect beforehand if your instance is going to cause you trouble when shh-ing into it is to ping it (see if it's responsive).

function getPing($addr) 
{
    //First try a direct ping
    $exec = exec("ping -c 3 -s 64 -t 64 ".$addr);
    $array = explode("/", end(explode("=", $exec )) );
    if(isset($pingVal[1]))
    {
        //There was a succesful ping response.
        $pingVal = ceil($array[1]);
    }
    else
    {
        //A ping could not be sent, maybe the server is blocking them, we'll try a generic request.
        $pingVal = callTarget($addr);
    }


    echo intval($pingVal)."ms";
    if($pingVal > ["threshold"])
    {
        return false;
    }
    return true;
}

function callTarget($target)
{
    $before = microtime();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $target);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    if (curl_exec($ch))
    {
        curl_close($ch);
        return (microtime()-$before)*1000;
    }
    else
    {
        curl_close($ch);
        return 9999;
    }
}

This method allows you to get a faster response on the state of your server, so you know if you are about to waste your time ssh-ing into it.

Perplexed answered 20/9, 2012 at 21:49 Comment(1)
I found the simple Ping idea really useful, saves 30 seconds timeout before throwing my custom exceptions. Thanks!Microspore
B
1

the libssh2 library doesn't do the connect() by itself so it doesn't have to provide a timeout for that. libssh2 does however offer timeouts for the functions it does provide...

Bottleneck answered 11/6, 2012 at 8:5 Comment(1)
There's a libssh2_session_set_timeout() function for libssh2 functions, but unfortunately not available in PHP.Astri
H
0

Using ping execution may cause problem if firewall on remote side block ping packets. Also this may cause problems on different environments or may be there other obstructions as acces restrictions about executing commands.

You can use ini_get, ini_set aprroach for 'default_socket_timeout' as previously well metioned by Patkos Csaba. But from my point of view is better to use test socket connection with specified timeout:

function sock_online_test($ip, $timeout=0.1, $port='22') {
    $fp = @fsockopen($ip, $port, $errno, $errstr, $timeout);
    if (!$fp) {
        return false;
    } else {
        fclose($fp);
        return true;
    }
}

$online = sock_online_test('192.168.1.1', 5);
// even domain may works
$online = sock_online_test('myserver.com', 5);
Hirz answered 9/8, 2018 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.