- add interactive mode support to Net_SSH2 and redo interactive support in Net_SSH1

git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@147 21d32557-59b3-4da0-833f-c5933fad653e
This commit is contained in:
Jim Wigginton 2011-02-28 05:24:09 +00:00
parent f6f9f3887d
commit 458f4f7ef0
3 changed files with 263 additions and 17 deletions

View File

@ -73,7 +73,7 @@ define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
/**
* SFTP channel constant
*
* Net_SSH2::exec() uses 0 and Net_SSH2::interactiveRead() / Net_SSH2::interactiveWrite() use 1.
* Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
*
* @see Net_SSH2::_send_channel_packet()
* @see Net_SSH2::_get_channel_packet()

View File

@ -16,15 +16,7 @@
* exit('Login Failed');
* }
*
* while (true) {
* echo $ssh->interactiveRead();
*
* $read = array(STDIN);
* $write = $except = NULL;
* if (stream_select($read, $write, $except, 0)) {
* $ssh->interactiveWrite(fread(STDIN, 1));
* }
* }
* echo $ssh->exec('ls -la');
* ?>
* </code>
*
@ -38,7 +30,9 @@
* exit('Login Failed');
* }
*
* echo $ssh->exec('ls -la');
* echo $ssh->read('%');
* $ssh->write("ls -la\r\n");
* echo $ssh->read('%');
* ?>
* </code>
*
@ -237,6 +231,20 @@ define('NET_SSH1_LOG_SIMPLE', 1);
define('NET_SSH1_LOG_COMPLEX', 2);
/**#@-*/
/**#@+
* @access public
* @see Net_SSH1::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
define('NET_SSH1_READ_SIMPLE', 1);
/**
* Returns when a string matching the regular expression $expect is found
*/
define('NET_SSH1_READ_REGEX', 2);
/**#@-*/
/**
* Pure-PHP implementation of SSHv1.
*
@ -396,6 +404,15 @@ class Net_SSH1 {
*/
var $message_log = array();
/**
* Interactive Buffer
*
* @see Net_SSH1::read()
* @var Array
* @access private
*/
var $interactive_buffer = '';
/**
* Default Constructor.
*
@ -657,7 +674,7 @@ class Net_SSH1 {
* @return mixed
* @access public
*/
function exec($cmd)
function exec($cmd, $block = true)
{
if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
@ -688,6 +705,10 @@ class Net_SSH1 {
return false;
}
if (!$block) {
return true;
}
$output = '';
$response = $this->_get_binary_packet();
@ -747,6 +768,59 @@ class Net_SSH1 {
return true;
}
/**
* Inputs a command into an interactive shell.
*
* @see Net_SSH1::interactiveWrite()
* @param String $cmd
* @return Boolean
* @access public
*/
function write($cmd)
{
return $this->interactiveWrite($cmd);
}
/**
* Returns the output of an interactive shell when there's a match for $expect
*
* $expect can take the form of a string literal or, if $mode == NET_SSH1_READ_REGEX,
* a regular expression.
*
* @see Net_SSH1::write()
* @param String $expect
* @param Integer $mode
* @return Boolean
* @access public
*/
function read($expect, $mode = NET_SSH1_READ_SIMPLE)
{
if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
while (true) {
if ($mode != NET_SSH1_READ_REGEX) {
$pos = strpos($this->interactiveBuffer, $expect);
} else {
$pos = preg_match($expect, $this->interactiveBuffer, $matches) ?
strpos($this->interactiveBuffer, $matches[0]) :
false;
}
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + 1);
}
$response = $this->_get_binary_packet();
$this->interactiveBuffer.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
}
}
/**
* Inputs a command into an interactive shell.
*
@ -778,7 +852,7 @@ class Net_SSH1 {
}
/**
* Reads the output of an interactive shell.
* Returns the output of an interactive shell when no more output is available.
*
* Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
* "", you're seeing ANSI escape codes. According to

View File

@ -35,8 +35,9 @@
* exit('Login Failed');
* }
*
* echo $ssh->exec('pwd');
* echo $ssh->exec('ls -la');
* echo $ssh->read('%');
* $ssh->write("ls -la\r\n");
* echo $ssh->read('%');
* ?>
* </code>
*
@ -104,6 +105,7 @@ require_once('Crypt/AES.php');
*/
define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH2_MASK_LOGIN', 0x00000002);
define('NET_SSH2_MASK_SHELL', 0x00000004);
/**#@-*/
/**#@+
@ -123,6 +125,7 @@ define('NET_SSH2_MASK_LOGIN', 0x00000002);
* @access private
*/
define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
define('NET_SSH2_CHANNEL_SHELL',1);
/**#@-*/
/**#@+
@ -139,6 +142,20 @@ define('NET_SSH2_LOG_SIMPLE', 1);
define('NET_SSH2_LOG_COMPLEX', 2);
/**#@-*/
/**#@+
* @access public
* @see Net_SSH2::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
define('NET_SSH2_READ_SIMPLE', 1);
/**
* Returns when a string matching the regular expression $expect is found
*/
define('NET_SSH2_READ_REGEX', 2);
/**#@-*/
/**
* Pure-PHP implementation of SSHv2.
*
@ -574,6 +591,15 @@ class Net_SSH2 {
*/
var $signature_format = '';
/**
* Interactive Buffer
*
* @see Net_SSH2::read()
* @var Array
* @access private
*/
var $interactive_buffer = '';
/**
* Default Constructor.
*
@ -1412,7 +1438,11 @@ class Net_SSH2 {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST';
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
$this->message_number_log[count($this->message_number_log) - 1]
);
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
@ -1447,7 +1477,11 @@ class Net_SSH2 {
}
if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE';
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
$this->message_number_log[count($this->message_number_log) - 1]
);
$this->message_log[count($this->message_log) - 1] = $logged;
}
@ -1639,6 +1673,144 @@ class Net_SSH2 {
}
}
/**
* Creates an interactive shell
*
* @see Net_SSH2::read()
* @see Net_SSH2::write()
* @return Boolean
* @access private
*/
function _initShell()
{
$this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
$packet_size = 0x4000;
$packet = pack('CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
if ($response === false) {
return false;
}
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack('CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
list(, $type) = unpack('C', $this->_string_shift($response, 1));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
break;
case NET_SSH2_MSG_CHANNEL_FAILURE:
default:
user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = pack('CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
if ($response === false) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
$this->bitmap |= NET_SSH2_MASK_SHELL;
return true;
}
/**
* Returns the output of an interactive shell
*
* Returns when there's a match for $expect, which can take the form of a string literal or,
* if $mode == NET_SSH2_READ_REGEX, a regular expression.
*
* @see Net_SSH2::read()
* @param String $expect
* @param Integer $mode
* @return String
* @access public
*/
function read($expect, $mode = NET_SSH2_READ_SIMPLE)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
while (true) {
if ($mode != NET_SSH2_READ_REGEX) {
$pos = strpos($this->interactiveBuffer, $expect);
} else {
$pos = preg_match($expect, $this->interactiveBuffer, $matches) ?
strpos($this->interactiveBuffer, $matches[0]) :
false;
}
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + 1);
}
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
$this->interactiveBuffer.= $response;
}
}
/**
* Inputs a command into an interactive shell.
*
* @see Net_SSH1::interactiveWrite()
* @param String $cmd
* @return Boolean
* @access public
*/
function write($cmd)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
}
/**
* Disconnect
*