TimeTrex/classes/modules/other/Telnet.class.php

327 lines
7.1 KiB
PHP
Raw Normal View History

2022-12-13 07:10:06 +01:00
<?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 );
}
}
?>