327 lines
7.1 KiB
PHP
327 lines
7.1 KiB
PHP
|
<?php /** @noinspection ALL */
|
||
|
|
||
|
/**
|
||
|
* Telnet class
|
||
|
*
|
||
|
* Used to execute remote commands via telnet connection
|
||
|
* Usess sockets functions and fgetc() to process result
|
||
|
*
|
||
|
* All methods throw Exceptions on error
|
||
|
*
|
||
|
* Written by Dalibor Andzakovic <dali@swerve.co.nz>
|
||
|
* Based on the code originally written by Marc Ennaji and extended by
|
||
|
* Matthias Blaser <mb@adfinis.ch>
|
||
|
*/
|
||
|
class Telnet {
|
||
|
|
||
|
private $host;
|
||
|
private $port;
|
||
|
private $timeout;
|
||
|
|
||
|
private $socket = null;
|
||
|
private $buffer = null;
|
||
|
private $prompt;
|
||
|
private $errno;
|
||
|
private $errstr;
|
||
|
|
||
|
private $NULL;
|
||
|
private $DC1;
|
||
|
private $WILL;
|
||
|
private $WONT;
|
||
|
private $DO;
|
||
|
private $DONT;
|
||
|
private $IAC;
|
||
|
|
||
|
const TELNET_ERROR = false;
|
||
|
const TELNET_OK = true;
|
||
|
|
||
|
/**
|
||
|
* Constructor. Initialises host, port and timeout parameters
|
||
|
* defaults to localhost port 23 (standard telnet port)
|
||
|
*
|
||
|
* @param string $host Host name or IP addres
|
||
|
* @param string $port TCP port number
|
||
|
* @param int $timeout Connection timeout in seconds
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function __construct( $host = '127.0.0.1', $port = '23', $timeout = 10 ) {
|
||
|
|
||
|
$this->host = $host;
|
||
|
$this->port = $port;
|
||
|
$this->timeout = $timeout;
|
||
|
|
||
|
// set some telnet special characters
|
||
|
$this->NULL = chr( 0 );
|
||
|
$this->DC1 = chr( 17 );
|
||
|
$this->WILL = chr( 251 );
|
||
|
$this->WONT = chr( 252 );
|
||
|
$this->DO = chr( 253 );
|
||
|
$this->DONT = chr( 254 );
|
||
|
$this->IAC = chr( 255 );
|
||
|
|
||
|
$this->connect();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destructor. Cleans up socket connection and command buffer
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function __destruct() {
|
||
|
|
||
|
// cleanup resources
|
||
|
$this->disconnect();
|
||
|
$this->buffer = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attempts connection to remote host. Returns TRUE if sucessful.
|
||
|
*
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public function connect() {
|
||
|
|
||
|
// check if we need to convert host to IP
|
||
|
if ( !preg_match( '/([0-9]{1,3}\\.){3,3}[0-9]{1,3}/', $this->host ) ) {
|
||
|
|
||
|
$ip = gethostbyname( $this->host );
|
||
|
|
||
|
if ( $this->host == $ip ) {
|
||
|
|
||
|
throw new Exception( "Cannot resolve $this->host" );
|
||
|
} else {
|
||
|
$this->host = $ip;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// attempt connection
|
||
|
$this->socket = fsockopen( $this->host, $this->port, $this->errno, $this->errstr, $this->timeout );
|
||
|
stream_set_timeout( $this->socket, $this->timeout );
|
||
|
|
||
|
|
||
|
if ( !$this->socket ) {
|
||
|
throw new Exception( "Cannot connect to $this->host on port $this->port" );
|
||
|
}
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Closes IP socket
|
||
|
*
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public function disconnect() {
|
||
|
if ( $this->socket ) {
|
||
|
if ( !fclose( $this->socket ) ) {
|
||
|
throw new Exception( "Error while closing telnet socket" );
|
||
|
}
|
||
|
$this->socket = null;
|
||
|
}
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Executes command and returns a string with result.
|
||
|
* This method is a wrapper for lower level private methods
|
||
|
*
|
||
|
* @param string $command Command to execute
|
||
|
* @return string Command result
|
||
|
*/
|
||
|
public function exec( $command ) {
|
||
|
|
||
|
$this->write( $command );
|
||
|
$this->waitPrompt();
|
||
|
|
||
|
return $this->getBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attempts login to remote host.
|
||
|
* This method is a wrapper for lower level private methods and should be
|
||
|
* modified to reflect telnet implementation details like login/password
|
||
|
* and line prompts. Defaults to standard unix non-root prompts
|
||
|
*
|
||
|
* @param string $username Username
|
||
|
* @param string $password Password
|
||
|
* @param string $login_prompt
|
||
|
* @param string $password_prompt
|
||
|
* @param string $prompt
|
||
|
* @return boolean
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function login( $username, $password, $login_prompt = 'login:', $password_prompt = 'Password:', $prompt = '#' ) {
|
||
|
|
||
|
try {
|
||
|
$this->setPrompt( $login_prompt );
|
||
|
$this->waitPrompt();
|
||
|
$this->write( $username );
|
||
|
$this->setPrompt( $password_prompt );
|
||
|
$this->waitPrompt();
|
||
|
$this->write( $password );
|
||
|
$this->setPrompt( $prompt );
|
||
|
$this->waitPrompt();
|
||
|
} catch ( Exception $e ) {
|
||
|
|
||
|
throw new Exception( "Login failed." );
|
||
|
}
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the string of characters to respond to.
|
||
|
* This should be set to the last character of the command line prompt
|
||
|
*
|
||
|
* @param string $s String to respond to
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public function setPrompt( $s = '$' ) {
|
||
|
$this->prompt = $s;
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets character from the socket
|
||
|
*
|
||
|
* @return bool|string|void
|
||
|
*/
|
||
|
private function getc() {
|
||
|
return fgetc( $this->socket );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clears internal command buffer
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function clearBuffer() {
|
||
|
$this->buffer = '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads characters from the socket and adds them to command buffer.
|
||
|
* Handles telnet control characters. Stops when prompt is ecountered.
|
||
|
*
|
||
|
* @param string $prompt
|
||
|
* @return boolean
|
||
|
*/
|
||
|
private function readTo( $prompt ) {
|
||
|
|
||
|
if ( !$this->socket ) {
|
||
|
throw new Exception( "Telnet connection closed" );
|
||
|
}
|
||
|
|
||
|
// clear the buffer
|
||
|
$this->clearBuffer();
|
||
|
|
||
|
do {
|
||
|
|
||
|
$c = $this->getc();
|
||
|
|
||
|
if ( $c === false ) {
|
||
|
throw new Exception( "Couldn't find the requested : '" . $prompt . "', it was not in the data returned from server : '" . $this->buffer . "'" );
|
||
|
}
|
||
|
|
||
|
// Interpreted As Command
|
||
|
if ( $c == $this->IAC ) {
|
||
|
if ( $this->negotiateTelnetOptions() ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// append current char to global buffer
|
||
|
$this->buffer .= $c;
|
||
|
|
||
|
// we've encountered the prompt. Break out of the loop
|
||
|
if ( strpos( $this->buffer, $prompt ) !== false ) {
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
} while ( $c != $this->NULL || $c != $this->DC1 );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write command to a socket
|
||
|
*
|
||
|
* @param string $buffer Stuff to write to socket
|
||
|
* @param boolean $addNewLine Default true, adds newline to the command
|
||
|
* @return boolean
|
||
|
*/
|
||
|
private function write( $buffer, $addNewLine = true ) {
|
||
|
|
||
|
if ( !$this->socket ) {
|
||
|
throw new Exception( "Telnet connection closed" );
|
||
|
}
|
||
|
|
||
|
// clear buffer from last command
|
||
|
$this->clearBuffer();
|
||
|
|
||
|
if ( $addNewLine == true ) {
|
||
|
$buffer .= "\n";
|
||
|
}
|
||
|
|
||
|
if ( !fwrite( $this->socket, $buffer ) < 0 ) {
|
||
|
throw new Exception( "Error writing to socket" );
|
||
|
}
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the content of the command buffer
|
||
|
*
|
||
|
* @return string Content of the command buffer
|
||
|
*/
|
||
|
private function getBuffer() {
|
||
|
// cut last line (is always prompt)
|
||
|
$buf = explode( "\n", $this->buffer );
|
||
|
unset( $buf[( count( $buf ) - 1 )] );
|
||
|
$buf = implode( "\n", $buf );
|
||
|
|
||
|
return trim( $buf );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Telnet control character magic
|
||
|
*
|
||
|
* @return boolean
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
private function negotiateTelnetOptions() {
|
||
|
|
||
|
$c = $this->getc();
|
||
|
|
||
|
if ( $c != $this->IAC ) {
|
||
|
|
||
|
if ( ( $c == $this->DO ) || ( $c == $this->DONT ) ) {
|
||
|
|
||
|
$opt = $this->getc();
|
||
|
fwrite( $this->socket, $this->IAC . $this->WONT . $opt );
|
||
|
} else {
|
||
|
if ( ( $c == $this->WILL ) || ( $c == $this->WONT ) ) {
|
||
|
|
||
|
$opt = $this->getc();
|
||
|
fwrite( $this->socket, $this->IAC . $this->DONT . $opt );
|
||
|
} else {
|
||
|
throw new Exception( 'Error: unknown control character ' . ord( $c ) );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
throw new Exception( 'Error: Something Wicked Happened' );
|
||
|
}
|
||
|
|
||
|
return self::TELNET_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads socket until prompt is encountered
|
||
|
*/
|
||
|
private function waitPrompt() {
|
||
|
return $this->readTo( $this->prompt );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
?>
|