* Based on the code originally written by Marc Ennaji and extended by * Matthias Blaser */ 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 ); } } ?>