From 1ab30836a6e145f4c7ae4517de06b2f38a93a1b8 Mon Sep 17 00:00:00 2001 From: Jim Wigginton Date: Sat, 3 Mar 2012 17:49:16 +0000 Subject: [PATCH] - make it so requests can timeout (thanks pmprojx!) git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@195 21d32557-59b3-4da0-833f-c5933fad653e --- phpseclib/Net/SFTP.php | 2 ++ phpseclib/Net/SSH2.php | 76 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 4049f8e9..7f841728 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -1818,6 +1818,8 @@ class Net_SFTP extends Net_SSH2 { */ function _get_sftp_packet() { + $this->curTimeout = false; + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 // SFTP packet length diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index ffa50630..c763066a 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -619,6 +619,22 @@ class Net_SSH2 { */ var $log_size; + /** + * Timeout + * + * @see Net_SSH2::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see Net_SSH2::_get_channel_packet() + * @access private + */ + var $curTimeout; + /** * Default Constructor. * @@ -1612,6 +1628,20 @@ class Net_SSH2 { return false; } + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param Mixed $timeout + */ + function setTimeout($timeout) + { + $this->timeout = $timeout; + $this->curTimeout = $timeout; + } + /** * Execute Command * @@ -1625,6 +1655,8 @@ class Net_SSH2 { */ function exec($command, $block = true) { + $this->curTimeout = $this->timeout; + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; } @@ -1730,7 +1762,6 @@ class Net_SSH2 { return false; } - $response = $this->_get_binary_packet(); if ($response === false) { user_error('Connection closed by server', E_USER_NOTICE); @@ -1782,6 +1813,8 @@ class Net_SSH2 { */ function read($expect, $mode = NET_SSH2_READ_SIMPLE) { + $this->curTimeout = $this->timeout; + if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { user_error('Operation disallowed prior to login()', E_USER_NOTICE); return false; @@ -1803,6 +1836,9 @@ class Net_SSH2 { return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); } $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); + if (is_bool($response)) { + return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; + } $this->interactiveBuffer.= $response; } @@ -2031,6 +2067,25 @@ class Net_SSH2 { } while (true) { + if ($this->curTimeout) { + $read = array($this->fsock); + $write = $except = NULL; + + stream_set_blocking($this->fsock, false); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $this->curTimeout)) { + stream_set_blocking($this->fsock, true); + $this->_close_channel($client_channel); + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + + stream_set_blocking($this->fsock, true); + } + $response = $this->_get_binary_packet(); if ($response === false) { user_error('Connection closed by server', E_USER_NOTICE); @@ -2048,11 +2103,11 @@ class Net_SSH2 { switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); - $this->server_channels[$client_channel] = $server_channel; + $this->server_channels[$channel] = $server_channel; $this->_string_shift($response, 4); // skip over (server) window size $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); - $this->packet_size_client_to_server[$client_channel] = $temp['packet_size_client_to_server']; - return true; + $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; + return $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: default: user_error('Unable to open channel', E_USER_NOTICE); @@ -2068,6 +2123,9 @@ class Net_SSH2 { user_error('Unable to request pseudo-terminal', E_USER_NOTICE); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } + case NET_SSH2_MSG_CHANNEL_CLOSE: + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + return $type != NET_SSH2_MSG_CHANNEL_CLOSE; } @@ -2300,7 +2358,15 @@ class Net_SSH2 { return false; } - while ($this->_get_channel_packet($client_channel) !== true); + $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + + $this->curTimeout = 0; + + while (!is_bool($this->_get_channel_packet($client_channel))); + + if ($this->bitmap & NET_SSH2_MASK_SHELL) { + $this->bitmap&= ~NET_SSH2_MASK_SHELL; + } } /**