SSH2 Wrapper Class
The following class takes some of the ideas used to create a contextual socket, and applied to the setup of a SSH2 stream. Currently, only password authentication is supported, but will be updated shortly. Support for SCP and SFTP will be included at a later time.
SSH2 Usage
$options = array(
'host' => '127.0.0.1',
'port' => 22,
'auth' =>
array(
'type' => SSH2::PASS,
'username' => 'user',
'password' => 'password',
'fingerprint' => 'xxxx',
'ssh_auth_pub' => '3sg325235y43yre',
'ssh_auth_priv' => '3sg325235y43yre',
'ssh_auth_priv_key' => '3sg325235y43yre',
)
);
try{
$ssh = new SSH2($options)->connect();
if($ssh->authenticate()){
/* For devices which support OpenSSH and OpenSSH2 Variants */
$exampleOutput = $ssh->exec(array('ls -lSha', 'cat ~/.bashrc'));
if(!$exampleOutput ){
echo $ssh->getLastError();
}else{
echo $exampleOutput ;
}
/* For devices which support net-sshd, vt100, etc */
$exampleOutput = $ssh->shell(array('ls -lSha', 'cat ~/.bashrc'));
if(!$exampleOutput ){
echo $ssh->getLastError();
}else{
echo $exampleOutput ;
}
}
$ssh->disconnect();
}
catch(SSH2FailedToConnectException $e){
print_r($e->getMessage());
}
catch(SSH2FailedToAuthenticate $e){
print_r($e->getMessage());
}
Get The SSH2 Class
class SSH2{
private static $connection;
private $error;
var $port = 22;
const PASS = 'password';
const PUBKEY = 'publickey';
public function __construct(Array $options){
foreach($options as $opt => $value){
$this->$opt = $value;
}
return $this;
}
final function xdisconnect($reason, $message, $language) {
printf("Server disconnected with reason code [%d] and message: %sn",
$reason, $message);
}
final function connect(){
$callbacks = array('disconnect' => 'xdisconnect');
self::$connection = @ssh2_connect($this->host, $this->port, NULL, $callbacks);
if(self::$connection === FALSE){
throw new SSH2FailedToConnectException($this->host, $this->port);
return false;
}
$this->fingerprint = @ssh2_fingerprint(self::$connection);
return true;
}
// sm business
final function authenticate(){
$method = "ssh2_auth_{$this->auth['type']}";
if(@$method(self::$connection, $this->auth['username'], $this->auth['password']) === FALSE){
throw new SSH2FailedToAuthenticate($this->host, $this->auth['username'], $this->auth['type']);
return false;
}else{
return true;
}
}
public function exec($cmd){
if(is_array($cmd)){
foreach($cmd as $command){
$stream = @ssh2_exec(self::$connection, $command);
$errorStream = @ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
/* Enable Blocking */
@stream_set_blocking($errorStream, true);
@stream_set_blocking($stream, true);
/* Grab Response */
$response .= stream_get_contents($stream);
$this->error .= stream_get_contents($errorStream);
}
}
else{
$stream = @ssh2_exec(self::$connection, $cmd);
$errorStream = @ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
/* Enable Blocking */
@stream_set_blocking($errorStream, true);
@stream_set_blocking($stream, true);
/* Grab Response */
$response .= stream_get_contents($stream);
$this->error .= stream_get_contents($errorStream);
}
if(is_null($response)){
return false;
}
return $response;
}
final function shell($cmd){
$stream = ssh2_shell (self::$connection, 'vt102', null, 80, 40, SSH2_TERM_UNIT_CHARS);
$output = NULL;
if(is_array($cmd)){
foreach($cmd as $command){
fwrite($stream, $command.PHP_EOL);
sleep(2);
while(( $res = stream_get_contents($stream, -1)) !== false){
$output .= $res;
if($res == ''){
break;
}
}
}
}
else{
fwrite($stream, $cmd.PHP_EOL);
sleep(2);
while(( $res = stream_get_contents($stream, -1)) !== false){
$output .= $res;
if($res == ''){
break;
}
}
}
fwrite($stream, 'exit'.PHP_EOL);
return $output;
}
public function disconnect(){
@ssh2_exec(self::$connection, 'exit');
}
public function getLastError(){
return $this->error;
}
}
/*
* Thrown if a class tries to access the XML parser's functionality
*
* @author sixeightzero
* @license http://opensource.org/licenses/gpl-license.php GNU General Public Licence
* @copyright (c) 2011 - Mike Mackintosh
* @version 0.1
* @package Zepnik Framework
*/
final class SSH2FailedToConnectException extends Exception
{
/**
* Sets the error message
*
* @todo Add logger output
*/
public function __construct($host, $port)
{
$message = "Failed to connect to host '{$host}' on port {$port}n";
// Call the parent constructor
parent::__construct($message);
}
}
final class SSH2FailedToAuthenticate extends Exception
{
/**
* Sets the error message
*
* @todo Add logger output
*/
public function __construct($host, $username, $type)
{
$message = "Failed to authenticate '{$username}' by '{$type}' on host '{$host}'n";
// Call the parent constructor
parent::__construct($message);
}
}
I think you should rewrite this with phpseclib, a pure PHP SSH implementation:
http://phpseclib.sourceforge.net/
The PECL SSH2 extension is hard to install and barely works anyway.
Aside from that, nice job!
Thanks for the suggestion!
I’ve used phpseclib in the past, but had no luck with netsshd/vt100 based systems such as ScreenOS devices. I’ll take another look and see if there has been any improvement.
I’d try the latest SVN. It fixes a number of issues the latest release version had. If you have any issues try posting on the support forums – the guy’s really helpful and you’d be helping to make an even better product!