From 2b5c2eb9abd2991f128e4d31bc3efabad65dd0d3 Mon Sep 17 00:00:00 2001 From: metaclassing Date: Fri, 4 Sep 2015 14:13:16 -0500 Subject: [PATCH 1/2] Tried to replicate SSHv2 exception support into SSHv1 --- phpseclib/Net/SSH1.php | 80 ++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/phpseclib/Net/SSH1.php b/phpseclib/Net/SSH1.php index 293ba931..99a1fa75 100644 --- a/phpseclib/Net/SSH1.php +++ b/phpseclib/Net/SSH1.php @@ -537,14 +537,15 @@ class SSH1 * Connect to an SSHv1 server * * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _connect() { $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); if (!$this->fsock) { - user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr")); - return false; + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); } $this->server_identification = $init_line = fgets($this->fsock, 255); @@ -555,20 +556,17 @@ class SSH1 } if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { - user_error('Can only connect to SSH servers'); - return false; + throw new \RuntimeException("Can only connect to SSH servers"); } if ($parts[1][0] != 1) { - user_error("Cannot connect to SSH $parts[1] servers"); - return false; + throw new \RuntimeException("Cannot connect to $parts[1] servers"); } fputs($this->fsock, $this->identifier."\r\n"); $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { - user_error('Expected SSH_SMSG_PUBLIC_KEY'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_PUBLIC_KEY'); } $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); @@ -652,8 +650,7 @@ class SSH1 $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_SESSION_KEY'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_SESSION_KEY'); } switch ($cipher) { @@ -682,8 +679,7 @@ class SSH1 $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { - user_error('Expected SSH_SMSG_SUCCESS'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); } $this->bitmap = self::MASK_CONNECTED; @@ -697,6 +693,8 @@ class SSH1 * @param string $username * @param string $password * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access public */ function login($username, $password = '') @@ -715,28 +713,25 @@ class SSH1 $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_USER'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_USER'); } $response = $this->_get_binary_packet(); if ($response === true) { - return false; + throw new \RuntimeException('Connection closed by server'); } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN; return true; } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { - user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); } $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_AUTH_PASSWORD'); } // remove the username and password from the last logged packet @@ -748,7 +743,7 @@ class SSH1 $response = $this->_get_binary_packet(); if ($response === true) { - return false; + throw new \RuntimeException('Connection closed by server'); } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN; @@ -756,8 +751,7 @@ class SSH1 } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { return false; } else { - user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); } } @@ -792,20 +786,19 @@ class SSH1 * @see \phpseclib\Net\SSH1::interactiveWrite() * @param string $cmd * @return mixed + * @throws \RuntimeException on error sending command * @access public */ function exec($cmd, $block = true) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_EXEC_CMD'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_CMD'); } if (!$block) { @@ -841,6 +834,8 @@ class SSH1 * @see \phpseclib\Net\SSH1::interactiveRead() * @see \phpseclib\Net\SSH1::interactiveWrite() * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _initShell() @@ -851,8 +846,7 @@ class SSH1 $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_REQUEST_PTY'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_REQUEST_PTY'); } $response = $this->_get_binary_packet(); @@ -861,15 +855,13 @@ class SSH1 return false; } if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { - user_error('Expected SSH_SMSG_SUCCESS'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); } $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_EXEC_SHELL'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_SHELL'); } $this->bitmap |= self::MASK_SHELL; @@ -902,18 +894,17 @@ class SSH1 * @param string $expect * @param int $mode * @return bool + * @throws \RuntimeException on connection error * @access public */ function read($expect, $mode = self::READ__SIMPLE) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $match = $expect; @@ -941,25 +932,23 @@ class SSH1 * @see \phpseclib\Net\SSH1::interactiveRead() * @param string $cmd * @return bool + * @throws \RuntimeException on connection error * @access public */ function interactiveWrite($cmd) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_STDIN'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_STDIN'); } return true; @@ -976,18 +965,17 @@ class SSH1 * * @see \phpseclib\Net\SSH1::interactiveRead() * @return string + * @throws \RuntimeException on connection error * @access public */ function interactiveRead() { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $read = array($this->fsock); From 029228b3fe930fa45060339d8f6d79bcf7b1fe6b Mon Sep 17 00:00:00 2001 From: metaclassing Date: Fri, 4 Sep 2015 14:20:51 -0500 Subject: [PATCH 2/2] I misinterpreted the meaning of === true and messed up quotes on one throw --- phpseclib/Net/SSH1.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpseclib/Net/SSH1.php b/phpseclib/Net/SSH1.php index 99a1fa75..89a67404 100644 --- a/phpseclib/Net/SSH1.php +++ b/phpseclib/Net/SSH1.php @@ -556,7 +556,7 @@ class SSH1 } if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { - throw new \RuntimeException("Can only connect to SSH servers"); + throw new \RuntimeException('Can only connect to SSH servers'); } if ($parts[1][0] != 1) { throw new \RuntimeException("Cannot connect to $parts[1] servers"); @@ -719,7 +719,7 @@ class SSH1 $response = $this->_get_binary_packet(); if ($response === true) { - throw new \RuntimeException('Connection closed by server'); + return false; } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN; @@ -743,7 +743,7 @@ class SSH1 $response = $this->_get_binary_packet(); if ($response === true) { - throw new \RuntimeException('Connection closed by server'); + return false; } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN;