From 0f96fae81879e6d8a19831a98063ecd9e2e1fdf9 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Sun, 14 Jul 2013 04:09:16 -0500 Subject: [PATCH] SSH2: channel handling adjustments - keep track of server -> client window size and look out for NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST packets as appropriate - send client -> server window size packets based on channel of received packet - not of desired channel - buffer incoming packets based on received packets channel - not on desired channel --- phpseclib/Net/SFTP.php | 2 ++ phpseclib/Net/SSH2.php | 64 +++++++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 9249421c..0a87493c 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -1514,6 +1514,8 @@ class Net_SFTP extends Net_SSH2 { $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $sftp_packet_size = 4096; // PuTTY uses 4096 + // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size-= strlen($handle) + 25; $i = 0; while ($sent < $size) { $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size); diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 489667be..045d15d4 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -73,10 +73,11 @@ * @see Net_SSH2::bitmap * @access private */ -define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); -define('NET_SSH2_MASK_LOGIN_REQ', 0x00000002); -define('NET_SSH2_MASK_LOGIN', 0x00000004); -define('NET_SSH2_MASK_SHELL', 0x00000008); +define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); +define('NET_SSH2_MASK_LOGIN_REQ', 0x00000002); +define('NET_SSH2_MASK_LOGIN', 0x00000004); +define('NET_SSH2_MASK_SHELL', 0x00000008); +define('NET_SSH2_MASK_WINDOW_ADJUST', 0X00000010); /**#@-*/ /**#@+ @@ -2552,7 +2553,8 @@ class Net_SSH2 { extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); $this->window_size_client_to_server[$channel] = $window_size; - $payload = $this->_get_binary_packet(); + $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); + } } @@ -2645,22 +2647,25 @@ class Net_SSH2 { user_error('Connection closed by server'); return false; } + if ($client_channel == -1 && $response === true) { + return true; + } if (!strlen($response)) { return ''; } + extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); + // resize the window, if appropriate - $this->window_size_server_to_client[$client_channel]-= strlen($response); - if ($this->window_size_server_to_client[$client_channel] < 0) { - $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size); + $this->window_size_server_to_client[$channel]-= strlen($response); + if ($this->window_size_server_to_client[$channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); if (!$this->_send_binary_packet($packet)) { return false; } - $this->window_size_server_to_client[$client_channel]+= $this->window_size; + $this->window_size_server_to_client[$channel]+= $this->window_size; } - extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); - switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { @@ -2692,15 +2697,17 @@ class Net_SSH2 { return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); } + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: /* - if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + if ($channel == NET_SSH2_CHANNEL_EXEC) { // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server // this actually seems to make things twice as fast. more to the point, the message right after // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. // in OpenSSH it slows things down but only by a couple thousandths of a second. - $this->_send_channel_packet($client_channel, chr(0)); + $this->_send_channel_packet($channel, chr(0)); } */ extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2708,10 +2715,10 @@ class Net_SSH2 { if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$channel][] = $data; break; case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: /* @@ -2729,10 +2736,10 @@ class Net_SSH2 { if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$client_channel])) { - $this->channel_buffers[$client_channel] = array(); + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); } - $this->channel_buffers[$client_channel][] = $data; + $this->channel_buffers[$channel][] = $data; break; case NET_SSH2_MSG_CHANNEL_REQUEST: extract(unpack('Nlength', $this->_string_shift($response, 4))); @@ -2940,9 +2947,28 @@ class Net_SSH2 { $this->_string_shift($data, $max_size) ); + $this->window_size_client_to_server[$client_channel]-= $max_size; + if (!$this->_send_binary_packet($packet)) { return false; } + + if ($max_size == $this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + // using an invalid channel will let the buffers be built up for the valid channels + $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ); + } + } + + if (strlen($data) >= $this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; + $this->_get_channel_packet(-1); + $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST; } $this->window_size_client_to_server[$client_channel]-= strlen($data);