Esempio n. 1
0
 /**
  * Gets channel data
  *
  * Returns the data as a string if it's available and false if not.
  *
  * @param $client_channel
  * @return Mixed
  * @access private
  */
 function _get_channel_packet($client_channel, $skip_extended = false)
 {
     if (!empty($this->channel_buffers[$client_channel])) {
         return array_shift($this->channel_buffers[$client_channel]);
     }
     while (true) {
         if ($this->curTimeout) {
             if ($this->curTimeout < 0) {
                 $this->is_timeout = true;
                 return true;
             }
             $read = array($this->fsock);
             $write = $except = null;
             $start = microtime(true);
             $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;
                 return true;
             }
             $elapsed = microtime(true) - $start;
             $this->curTimeout -= $elapsed;
         }
         $response = $this->_get_binary_packet();
         if ($response === false) {
             user_error('Connection closed by server');
             return false;
         }
         if ($client_channel == -1 && $response === true) {
             return true;
         }
         if (!strlen($response)) {
             return '';
         }
         extract(unpack('Ctype', $this->_string_shift($response, 1)));
         if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
             extract(unpack('Nlength', $this->_string_shift($response, 4)));
         } else {
             extract(unpack('Nchannel', $this->_string_shift($response, 4)));
         }
         // will not be setup yet on incoming channel open request
         if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
             $this->window_size_server_to_client[$channel] -= strlen($response);
             // resize the window, if appropriate
             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[$channel] += $this->window_size;
             }
             switch ($this->channel_status[$channel]) {
                 case NET_SSH2_MSG_CHANNEL_OPEN:
                     switch ($type) {
                         case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
                             extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
                             $this->server_channels[$channel] = $server_channel;
                             extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
                             if ($window_size < 0) {
                                 $window_size &= 0x7fffffff;
                                 $window_size += 2147483648.0;
                             }
                             $this->window_size_client_to_server[$channel] = $window_size;
                             $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
                             $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
                             $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
                             $this->_on_channel_open();
                             return $result;
                             //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
                         //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
                         default:
                             user_error('Unable to open channel');
                             return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                     }
                     break;
                 case NET_SSH2_MSG_CHANNEL_REQUEST:
                     switch ($type) {
                         case NET_SSH2_MSG_CHANNEL_SUCCESS:
                             return true;
                         case NET_SSH2_MSG_CHANNEL_FAILURE:
                             return false;
                         default:
                             user_error('Unable to fulfill channel request');
                             return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                     }
                 case NET_SSH2_MSG_CHANNEL_CLOSE:
                     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 ($channel == self::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($channel, chr(0));
                 }
                 */
                 extract(unpack('Nlength', $this->_string_shift($response, 4)));
                 $data = $this->_string_shift($response, $length);
                 if ($channel == self::CHANNEL_AGENT_FORWARD) {
                     $agent_response = $this->agent->_forward_data($data);
                     if (!is_bool($agent_response)) {
                         $this->_send_channel_packet($channel, $agent_response);
                     }
                     break;
                 }
                 if ($client_channel == $channel) {
                     return $data;
                 }
                 if (!isset($this->channel_buffers[$channel])) {
                     $this->channel_buffers[$channel] = array();
                 }
                 $this->channel_buffers[$channel][] = $data;
                 break;
             case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
                 /*
                 if ($client_channel == self::CHANNEL_EXEC) {
                     $this->_send_channel_packet($client_channel, chr(0));
                 }
                 */
                 // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
                 extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
                 $data = $this->_string_shift($response, $length);
                 $this->stdErrorLog .= $data;
                 if ($skip_extended || $this->quiet_mode) {
                     break;
                 }
                 if ($client_channel == $channel) {
                     return $data;
                 }
                 if (!isset($this->channel_buffers[$channel])) {
                     $this->channel_buffers[$channel] = array();
                 }
                 $this->channel_buffers[$channel][] = $data;
                 break;
             case NET_SSH2_MSG_CHANNEL_REQUEST:
                 extract(unpack('Nlength', $this->_string_shift($response, 4)));
                 $value = $this->_string_shift($response, $length);
                 switch ($value) {
                     case 'exit-signal':
                         $this->_string_shift($response, 1);
                         extract(unpack('Nlength', $this->_string_shift($response, 4)));
                         $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
                         $this->_string_shift($response, 1);
                         extract(unpack('Nlength', $this->_string_shift($response, 4)));
                         if ($length) {
                             $this->errors[count($this->errors)] .= "\r\n" . $this->_string_shift($response, $length);
                         }
                         $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
                         $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
                         $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
                         break;
                     case 'exit-status':
                         extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
                         $this->exit_status = $exit_status;
                         // "The client MAY ignore these messages."
                         // -- http://tools.ietf.org/html/rfc4254#section-6.10
                         break;
                     default:
                         // "Some systems may not implement signals, in which case they SHOULD ignore this message."
                         //  -- http://tools.ietf.org/html/rfc4254#section-6.9
                         break;
                 }
                 break;
             case NET_SSH2_MSG_CHANNEL_CLOSE:
                 $this->curTimeout = 0;
                 if ($this->bitmap & self::MASK_SHELL) {
                     $this->bitmap &= ~self::MASK_SHELL;
                 }
                 if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
                     $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
                 }
                 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
                 return true;
             case NET_SSH2_MSG_CHANNEL_EOF:
                 break;
             default:
                 user_error('Error reading channel data');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
         }
     }
 }