From 96c4c3bc5810e8bed980504e4fefcb49ef437a77 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Mon, 28 Sep 2020 06:17:49 -0500 Subject: [PATCH 1/7] SSH2: add setKeepAlive() method --- phpseclib/Net/SSH2.php | 45 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index d3cbc1f4..17125801 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -692,6 +692,14 @@ class Net_SSH2 */ var $curTimeout; + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + * @access private + */ + var $keepAlive; + /** * Real-time log file pointer * @@ -2691,6 +2699,19 @@ class Net_SSH2 $this->timeout = $this->curTimeout = $timeout; } + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param mixed $timeout + * @access public + */ + function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + /** * Get the output from stdError * @@ -3684,8 +3705,15 @@ class Net_SSH2 $read = array($this->fsock); $write = $except = null; - if (!$this->curTimeout) { - @stream_select($read, $write, $except, null); + if ($this->curTimeout <= 0) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + continue; + } + } } else { if ($this->curTimeout < 0) { $this->is_timeout = true; @@ -3696,8 +3724,21 @@ class Net_SSH2 $write = $except = null; $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + continue; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + $sec = floor($this->curTimeout); $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { $this->is_timeout = true; From 194e64244824eee9fd37fc8fa0605fef26f951ba Mon Sep 17 00:00:00 2001 From: terrafrost Date: Tue, 15 Dec 2020 08:25:42 -0600 Subject: [PATCH 2/7] SSH2: handle keepalive@openssh.com packets --- phpseclib/Net/SSH2.php | 112 +++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 50 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 17125801..ce007b06 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3351,6 +3351,57 @@ class Net_SSH2 */ function _get_binary_packet($skip_channel_filter = false) { + if ($skip_channel_filter) { + $read = array($this->fsock); + $write = $except = null; + + if ($this->curTimeout <= 0) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + return $this->_get_binary_packet(true); + } + } + } else { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + return $this->_get_binary_packet(true); + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + } + if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; user_error('Connection closed prematurely'); @@ -3502,9 +3553,19 @@ class Net_SSH2 // only called when we've already logged in if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) { switch (ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (strlen($payload) == 31) { + extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (ord(substr($payload, 9 + $length))) { // want reply + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + break; case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - case NET_SSH2_MSG_CHANNEL_REQUEST: case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_EOF: if (!$skip_channel_filter && !empty($this->server_channels)) { @@ -3702,55 +3763,6 @@ class Net_SSH2 $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { - $read = array($this->fsock); - $write = $except = null; - - if ($this->curTimeout <= 0) { - if ($this->keepAlive <= 0) { - @stream_select($read, $write, $except, null); - } else { - if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); - continue; - } - } - } else { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return true; - } - - $read = array($this->fsock); - $write = $except = null; - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - - if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { - if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; - $this->curTimeout-= $elapsed; - continue; - } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; - $this->curTimeout-= $elapsed; - } - - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); - } - return true; - } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; - $this->curTimeout-= $elapsed; - } - $response = $this->_get_binary_packet(true); if ($response === false) { $this->bitmap = 0; From d904f08a46a36e17ee74ca49059f20961f37a279 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Tue, 15 Dec 2020 08:43:21 -0600 Subject: [PATCH 3/7] SSH2: last merge should have used microtime(true) but didn't --- phpseclib/Net/SSH2.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index c5f2e015..3f651190 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3358,16 +3358,16 @@ class SSH2 $read = array($this->fsock); $write = $except = null; - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $start = microtime(true); if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; return $this->_get_binary_packet(true); } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; } @@ -3382,7 +3382,7 @@ class SSH2 } return true; } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; } } From f0501ff72b5f3b90c0e66ff5d1b886771089af38 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Tue, 15 Dec 2020 23:11:54 -0600 Subject: [PATCH 4/7] SSH2: get_binary_packet() doesn't know about channel type --- phpseclib/Net/SSH2.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index ce007b06..fb458466 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3392,9 +3392,6 @@ class Net_SSH2 // on windows this returns a "Warning: Invalid CRT parameters detected" error if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { $this->is_timeout = true; - if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); - } return true; } $elapsed = strtok(microtime(), ' ') + strtok('') - $start; @@ -3764,6 +3761,9 @@ class Net_SSH2 $this->binary_packet_buffer = false; } else { $response = $this->_get_binary_packet(true); + if ($response === true && $this->is_timeout && $client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); From 8d567cdf29531bfb425e4f3f09d7b53cf2974924 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Wed, 16 Dec 2020 08:26:03 -0600 Subject: [PATCH 5/7] SSH2: keepalive tweak --- phpseclib/Net/SSH2.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index fb458466..5ebad708 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3761,8 +3761,11 @@ class Net_SSH2 $this->binary_packet_buffer = false; } else { $response = $this->_get_binary_packet(true); - if ($response === true && $this->is_timeout && $client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); + if ($response === true && $this->is_timeout)) { + if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } + return true; } if ($response === false) { $this->bitmap = 0; From 0399faa86b9c046be49a9deed2b28d28936afc13 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Wed, 16 Dec 2020 09:10:35 -0600 Subject: [PATCH 6/7] SSH2: syntax error --- phpseclib/Net/SSH2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 5ebad708..ca559af5 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3761,7 +3761,7 @@ class Net_SSH2 $this->binary_packet_buffer = false; } else { $response = $this->_get_binary_packet(true); - if ($response === true && $this->is_timeout)) { + if ($response === true && $this->is_timeout) { if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { $this->_close_channel($client_channel); } From 47e1ce60b316c49bd6c49c947731681598ddeed2 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Wed, 16 Dec 2020 09:13:05 -0600 Subject: [PATCH 7/7] SSH2: syntax error --- phpseclib/Net/SSH2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 76ce6357..f7a099a8 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -3689,7 +3689,7 @@ class SSH2 $this->binary_packet_buffer = false; } else { $response = $this->get_binary_packet(true); - if ($response === true && $this->is_timeout) + if ($response === true && $this->is_timeout) { if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { $this->close_channel($client_channel); }