- fixed a bug in isPrime (thanks greyhoundx!)

- Net_SSH2::exec() should now be faster
- better channel management


git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@70 21d32557-59b3-4da0-833f-c5933fad653e
This commit is contained in:
Jim Wigginton 2009-12-14 18:14:54 +00:00
parent 8379f46a77
commit 99c0e29fdf
3 changed files with 163 additions and 140 deletions

View File

@ -69,7 +69,7 @@
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVI Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: BigInteger.php,v 1.18 2009-12-04 19:12:18 terrafrost Exp $
* @version $Id: BigInteger.php,v 1.19 2009-12-14 18:14:54 terrafrost Exp $
* @link http://pear.php.net/package/Math_BigInteger
*/
@ -2776,11 +2776,15 @@ class Math_BigInteger {
$two = new Math_BigInteger(2);
}
if ($this->equals($one)) {
return false;
}
// see HAC 4.4.1 "Random search for probable primes"
for ($i = 0; $i < count($primes); $i++) {
list(, $r) = $this->divide($primes[$i]);
if ($r->equals($zero)) {
return false;
return $this->equals($primes[$i]);
}
}
@ -2792,6 +2796,7 @@ class Math_BigInteger {
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
$s = 0;
// if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
$r->value = bcdiv($r->value, 2);
$s++;

View File

@ -48,7 +48,7 @@
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMIX Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: SFTP.php,v 1.12 2009-11-26 20:12:17 terrafrost Exp $
* @version $Id: SFTP.php,v 1.13 2009-12-14 18:14:54 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
@ -71,6 +71,17 @@ define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE);
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.
*
* @see Net_SSH2::_send_channel_packet()
* @see Net_SSH2::_get_channel_packet()
* @access private
*/
define('NET_SFTP_CHANNEL', 2);
/**#@+
* @access public
* @see Net_SFTP::put()
@ -307,56 +318,33 @@ class Net_SFTP extends Net_SSH2 {
}
$packet = pack('CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', $this->client_channel, $this->window_size, $this->packet_size_server_to_client);
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, $this->packet_size_server_to_client);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
$this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
$this->_string_shift($response, 4); // skip over client channel
extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
$this->server_channels[$this->client_channel] = $server_channel;
$this->_string_shift($response, 4); // skip over (server) window size
extract(unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)));
$this->packet_size_client_to_server = $packet_size_client_to_server;
break;
case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
user_error('Unable to open channel', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = pack('CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST, $server_channel, strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp');
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
$this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
extract(unpack('Ctype', $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 initiate SFTP channel', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
return false;
@ -1160,7 +1148,7 @@ class Net_SFTP extends Net_SSH2 {
* @param Integer $type
* @param String $data
* @see Net_SFTP::_get_sftp_packet()
* @see Net_SFTP::_send_channel_packet()
* @see Net_SSH2::_send_channel_packet()
* @return Boolean
* @access private
*/
@ -1171,7 +1159,7 @@ class Net_SFTP extends Net_SSH2 {
pack('NCa*', strlen($data) + 1, $type, $data);
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
$result = $this->_send_channel_packet($packet);
$result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SFTP_LOGGING')) {
@ -1202,7 +1190,7 @@ class Net_SFTP extends Net_SSH2 {
// SFTP packet length
while (strlen($this->packet_buffer) < 4) {
$temp = $this->_get_channel_packet();
$temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
if (is_bool($temp)) {
$this->packet_type = false;
$this->packet_buffer = '';
@ -1216,7 +1204,7 @@ class Net_SFTP extends Net_SSH2 {
// SFTP packet type and data payload
while ($tempLength > 0) {
$temp = $this->_get_channel_packet();
$temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
if (is_bool($temp)) {
$this->packet_type = false;
$this->packet_buffer = '';

View File

@ -60,7 +60,7 @@
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: SSH2.php,v 1.30 2009-12-06 07:26:52 terrafrost Exp $
* @version $Id: SSH2.php,v 1.31 2009-12-14 18:14:54 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
@ -106,6 +106,25 @@ define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH2_MASK_LOGIN', 0x00000002);
/**#@-*/
/**#@+
* Channel constants
*
* RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
* to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
* a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
* recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
* would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
* The 'recipient channel' is the channel number given in the original
* open request, and 'sender channel' is the channel number allocated by
* the other side.
*
* @see Net_SSH2::_send_channel_packet()
* @see Net_SSH2::_get_channel_packet()
* @access private
*/
define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
/**#@-*/
/**#@+
* @access public
* @see Net_SSH2::getLog()
@ -433,19 +452,6 @@ class Net_SSH2 {
*/
var $get_seq_no = 0;
/**
* The Client Channel
*
* Net_SSH2::exec() uses 0, although since Net_SSH2::exec() doesn't send NET_SSH2_CHANNEL_DATA packets,
* Net_SSH2::_send_channel_packet() isn't actually called by any function in Net/SSH2.php. Classes that
* extend Net_SSH2, however, might call that function, hence it's existence.
*
* @var Integer
* @see Net_SSH2::_send_channel_packet
* @access private
*/
var $client_channel = 1;
/**
* Server Channels
*
@ -459,16 +465,39 @@ class Net_SSH2 {
var $server_channels = array();
/**
* Packet Size
* Channel Buffers
*
* Maximum packet size
* If a client requests a packet from one channel but receives two packets from another those packets should
* be placed in a buffer
*
* @see Net_SSH2::_send_channel_packet()
* @see Net_SSH2::_get_channel_packet()
* @see Net_SSH2::exec()
* @var Integer
* @var Array
* @access private
*/
var $packet_size_client_to_server = 0;
var $channel_buffers = array();
/**
* Channel Status
*
* Contains the type of the last sent message
*
* @see Net_SSH2::_get_channel_packet()
* @var Array
* @access private
*/
var $channel_status = array();
/**
* Packet Size
*
* Maximum packet size indexed by channel
*
* @see Net_SSH2::_send_channel_packet()
* @var Array
* @access private
*/
var $packet_size_client_to_server = array();
/**
* Message Number Log
@ -488,6 +517,15 @@ class Net_SSH2 {
*/
var $message_log = array();
/**
* Timeout Value
*
* @see Net_SSH2::interactiveRead()
* @var Integer
* @access private
*/
var $timeout = 400000; // ie. 0.1 seconds
/**
* Default Constructor.
*
@ -1396,15 +1434,6 @@ class Net_SSH2 {
return false;
}
// RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
// to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
// a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
// recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
// would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
// The 'recipient channel' is the channel number given in the original
// open request, and 'sender channel' is the channel number allocated by
// the other side.
$client_channel = 0; // PuTTy uses 0x100
// RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
// be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
// honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
@ -1415,89 +1444,42 @@ class Net_SSH2 {
$packet_size = 0x4000;
$packet = pack('CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', $client_channel, $window_size, $packet_size);
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $window_size, $packet_size);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
$this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
$this->_string_shift($response, 4); // skip over client channel
extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
$this->server_channels[$client_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 = $temp['packet_size_client_to_server'];
break;
case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
user_error('Unable to open channel', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack('CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST, $server_channel, 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;
}
extract(unpack('Ctype', $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);
}
// sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, slows things down
// although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
// SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
// "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
$packet = pack('CNNa*CNa*',
NET_SSH2_MSG_CHANNEL_REQUEST, $server_channel, strlen('exec'), 'exec', 1, strlen($command), $command);
NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
$this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
extract(unpack('Ctype', $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 start execution of command', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
$output = '';
while (true) {
$temp = $this->_get_channel_packet();
$temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
switch (true) {
case $temp === true:
return $output;
@ -1688,11 +1670,16 @@ class Net_SSH2 {
*
* 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()
function _get_channel_packet($client_channel)
{
if (!empty($this->channel_buffers[$client_channel])) {
return array_shift($this->channel_buffers[$client_channel]);
}
while (true) {
$response = $this->_get_binary_packet();
if ($response === false) {
@ -1700,15 +1687,59 @@ class Net_SSH2 {
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
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[$client_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;
//case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
default:
user_error('Unable to open channel', E_USER_NOTICE);
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:
default:
user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA:
$this->_string_shift($response, 4); // skip over client channel
if ($client_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));
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
return $this->_string_shift($response, $length);
$data = $this->_string_shift($response, $length);
if ($client_channel == $channel) {
return $data;
}
if (!isset($this->channel_buffers[$client_channel])) {
$this->channel_buffers[$client_channel] = array();
}
$this->channel_buffers[$client_channel][] = $data;
break;
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
$this->_string_shift($response, 4); // skip over client channel
if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
$this->_send_channel_packet($client_channel, chr(0));
}
extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
$data = $this->_string_shift($response, $length);
switch ($data_type_code) {
@ -1717,7 +1748,6 @@ class Net_SSH2 {
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
$this->_string_shift($response, 4); // skip over client channel
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length);
switch ($value) {
@ -1728,17 +1758,15 @@ class Net_SSH2 {
$this->_string_shift($response, 1);
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n" . $this->_string_shift($response, $length);
case 'exit-status':
//case 'exit-status':
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 2;
break;
}
break;
case NET_SSH2_MSG_CHANNEL_CLOSE:
extract(unpack('Nclient_channel', $this->_string_shift($response, 4)));
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
$this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
return true;
case NET_SSH2_MSG_CHANNEL_EOF:
break;
@ -1815,17 +1843,19 @@ class Net_SSH2 {
*
* Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
*
* @param Integer $client_channel
* @param String $data
* @return Boolean
* @access private
*/
function _send_channel_packet($data)
function _send_channel_packet($client_channel, $data)
{
while (strlen($data) > $this->packet_size_client_to_server) {
while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
$packet = pack('CN2a*',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$this->client_channel],
$this->packet_size_client_to_server,
$this->_string_shift($data, $this->packet_size_client_to_server)
$this->server_channels[$client_channel],
$this->packet_size_client_to_server[$client_channel],
$this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
);
if (!$this->_send_binary_packet($packet)) {
@ -1834,7 +1864,7 @@ class Net_SSH2 {
}
return $this->_send_binary_packet(pack('CN2a*',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$this->client_channel],
$this->server_channels[$client_channel],
strlen($data),
$data));
}