- improved error logging capability

git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@86 21d32557-59b3-4da0-833f-c5933fad653e
This commit is contained in:
Jim Wigginton 2010-02-12 23:02:13 +00:00
parent 66489c3733
commit 76e4066e94
2 changed files with 167 additions and 57 deletions

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.15 2010-02-11 16:17:40 terrafrost Exp $
* @version $Id: SFTP.php,v 1.16 2010-02-12 23:02:13 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
@ -202,6 +202,16 @@ class Net_SFTP extends Net_SSH2 {
*/
var $packet_log = array();
/**
* Error information
*
* @see Net_SFTP::getSFTPErrors()
* @see Net_SFTP::getLastSFTPError()
* @var String
* @access private
*/
var $errors = array();
/**
* Default Constructor.
*
@ -253,7 +263,14 @@ class Net_SFTP extends Net_SSH2 {
);
$this->status_codes = array(
0 => 'NET_SFTP_STATUS_OK',
1 => 'NET_SFTP_STATUS_EOF'
1 => 'NET_SFTP_STATUS_EOF',
2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
4 => 'NET_SFTP_STATUS_FAILURE',
5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
6 => 'NET_SFTP_STATUS_NO_CONNECTION',
7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED'
);
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
// the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
@ -477,10 +494,8 @@ class Net_SFTP extends Net_SSH2 {
$realpath = $this->_string_shift($response, $length);
break;
case NET_SFTP_STATUS:
// skip over the status code - hopefully the error message will give us all the info we need, anyway
$this->_string_shift($response, 4);
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length);
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
@ -522,6 +537,8 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -532,12 +549,19 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
$this->_get_sftp_packet();
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
$this->pwd = $dir;
return true;
}
@ -575,6 +599,8 @@ class Net_SFTP extends Net_SSH2 {
break;
case NET_SFTP_STATUS:
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -608,7 +634,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length);
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
break 2;
@ -624,12 +650,19 @@ class Net_SFTP extends Net_SSH2 {
// "The client MUST release all resources associated with the handle regardless of the status."
// -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
$this->_get_sftp_packet();
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
return $contents;
}
@ -668,12 +701,18 @@ class Net_SFTP extends Net_SSH2 {
-- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
*/
$this->_get_sftp_packet();
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
}
// rather than return what the permissions *should* be, we'll return what they actually are. this will also
// tell us if the file actually exists.
// incidentally ,SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
@ -688,6 +727,8 @@ class Net_SFTP extends Net_SSH2 {
$attrs = $this->_parseAttributes($response);
return $attrs['permissions'];
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
@ -728,7 +769,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length);
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
@ -766,6 +807,8 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
@ -815,9 +858,8 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_string_shift($response, 4);
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length);
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -857,12 +899,18 @@ class Net_SFTP extends Net_SSH2 {
$i++;
}
while ($i-- > 0){
$this->_get_sftp_packet();
while ($i--) {
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
}
}
if ($mode == NET_SFTP_LOCAL_FILE) {
@ -873,12 +921,19 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
$this->_get_sftp_packet();
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
return true;
}
@ -916,6 +971,8 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -933,13 +990,14 @@ class Net_SFTP extends Net_SSH2 {
$attrs = $this->_parseAttributes($response);
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
if ($local_file !== false) {
$fp = fopen($local_file, 'w');
if (!$fp) {
@ -968,10 +1026,9 @@ class Net_SFTP extends Net_SSH2 {
}
break;
case NET_SFTP_STATUS:
$this->_string_shift($response, 4);
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_FXP_STATUS:\r\n" . $this->_string_shift($response, $length);
return false;
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
break 2;
default:
user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE);
return false;
@ -982,12 +1039,19 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
$this->_get_sftp_packet();
$response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
if (isset($content)) {
return $content;
}
@ -1025,10 +1089,15 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return $status == NET_SFTP_STATUS_OK;
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
return true;
}
/**
@ -1063,10 +1132,15 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return $status == NET_SFTP_STATUS_OK;
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
}
return true;
}
/**
@ -1198,12 +1272,6 @@ class Net_SFTP extends Net_SSH2 {
$this->packet_type = ord($this->_string_shift($this->packet_buffer));
if (defined('NET_SFTP_LOGGING')) {
$this->packet_type_log[] = '<- ' . $this->packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) . 's)';
$this->packet_log[] = $packet;
}
if ($this->request_id !== false) {
$this->_string_shift($this->packet_buffer, 4); // remove the request id
$length-= 5; // account for the request id and the packet type
@ -1211,7 +1279,15 @@ class Net_SFTP extends Net_SSH2 {
$length-= 1; // account for the packet type
}
return $this->_string_shift($this->packet_buffer, $length);
$packet = $this->_string_shift($this->packet_buffer, $length);
if (defined('NET_SFTP_LOGGING')) {
$this->packet_type_log[] = '<- ' . $this->packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) . 's)';
$this->packet_log[] = $packet;
}
return $packet;
}
/**
@ -1239,6 +1315,28 @@ class Net_SFTP extends Net_SSH2 {
return $return;
}
/**
* Returns all errors
*
* @return String
* @access public
*/
function getSFTPErrors()
{
return $this->sftp_errors;
}
/**
* Returns the last error
*
* @return String
* @access public
*/
function getLastSFTPError()
{
return $this->sftp_errors[count($this->sftp_errors) - 1];
}
/**
* Get supported SFTP versions
*

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.36 2010-02-11 07:02:51 terrafrost Exp $
* @version $Id: SSH2.php,v 1.37 2010-02-12 23:02:13 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
@ -176,13 +176,14 @@ class Net_SSH2 {
var $bitmap = 0;
/**
* Debug Info
* Error information
*
* @see Net_SSH2::getDebugInfo()
* @see Net_SSH2::getErrors()
* @see Net_SSH2::getLastError()
* @var String
* @access private
*/
var $debug_info = '';
var $errors = array();
/**
* Server Identifier
@ -637,7 +638,7 @@ class Net_SSH2 {
$temp = '';
while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
if (substr($temp, -2) == "\r\n") {
$this->debug_info.= $temp;
$this->errors[] = $temp;
$temp = '';
}
$temp.= fgets($this->fsock, 255);
@ -665,7 +666,7 @@ class Net_SSH2 {
}
$this->server_identifier = trim($temp);
$this->debug_info = utf8_decode($this->debug_info);
$this->errors[] = utf8_decode($this->debug_info);
if ($matches[1] != '1.99' && $matches[1] != '2.0') {
user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
@ -1400,7 +1401,7 @@ class Net_SSH2 {
$this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_PASSWD_CHANGEREQ:\r\n" . utf8_decode($this->_string_shift($response, $length));
$this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employees multi-factor authentication
@ -1462,7 +1463,7 @@ class Net_SSH2 {
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_FAILURE:\r\n" . $this->_string_shift($response, $length);
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key blob and the public key algorithm name are as
@ -1675,7 +1676,7 @@ class Net_SSH2 {
case NET_SSH2_MSG_DISCONNECT:
$this->_string_shift($payload, 1);
extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
$this->debug_info.= "\r\n\r\nSSH_MSG_DISCONNECT:\r\n" . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->bitmask = 0;
return false;
case NET_SSH2_MSG_IGNORE:
@ -1684,7 +1685,7 @@ class Net_SSH2 {
case NET_SSH2_MSG_DEBUG:
$this->_string_shift($payload, 2);
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_DEBUG:\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
$payload = $this->_get_binary_packet();
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
@ -1703,7 +1704,7 @@ class Net_SSH2 {
if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
$this->_string_shift($payload, 1);
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_USERAUTH_BANNER:\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
$payload = $this->_get_binary_packet();
}
@ -1713,7 +1714,7 @@ class Net_SSH2 {
case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
$this->_string_shift($payload, 1);
extract(unpack('Nlength', $this->_string_shift($payload)));
$this->debug_info.= "\r\n\r\nSSH_MSG_GLOBAL_REQUEST:\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
@ -1724,7 +1725,7 @@ class Net_SSH2 {
case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
$this->_string_shift($payload, 1);
extract(unpack('N', $this->_string_shift($payload, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_OPEN:\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
$this->_string_shift($payload, 4); // skip over client channel
extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
@ -1825,7 +1826,7 @@ class Net_SSH2 {
$data = $this->_string_shift($response, $length);
switch ($data_type_code) {
case NET_SSH2_EXTENDED_DATA_STDERR:
$this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_EXTENDED_DATA (SSH_EXTENDED_DATA_STDERR):\r\n" . $data;
$this->errors[] = 'SSH_MSG_CHANNEL_EXTENDED_DATA (SSH_EXTENDED_DATA_STDERR): ' . $data;
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
@ -1835,10 +1836,12 @@ class Net_SSH2 {
case 'exit-signal':
$this->_string_shift($response, 1);
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->debug_info.= "\r\n\r\nSSH_MSG_CHANNEL_REQUEST (exit-signal):\r\nSIG" . $this->_string_shift($response, $length);
$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)));
$this->debug_info.= "\r\n" . $this->_string_shift($response, $length);
if ($length) {
$this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
}
//case 'exit-status':
default:
// "Some systems may not implement signals, in which case they SHOULD ignore this message."
@ -2072,16 +2075,25 @@ class Net_SSH2 {
}
/**
* Returns Debug Information
*
* If any debug information is sent by the server, this function can be used to access it.
* Returns all errors
*
* @return String
* @access public
*/
function getDebugInfo()
function getErrors()
{
return $this->debug_info;
return $this->errors;
}
/**
* Returns the last error
*
* @return String
* @access public
*/
function getLastError()
{
return $this->errors[count($this->errors) - 1];
}
/**