* 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 int $port TCP port number * @param int $timeout Connection timeout in seconds * @return void */ 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); 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 * @return boolean */ public function login($username, $password) { try{ $this->setPrompt('login:'); $this->waitPrompt(); $this->write($username); $this->setPrompt('Password:'); $this->waitPrompt(); $this->write($password); $this->setPrompt(); $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 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 : '" . $buf . "'"); } // 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 ((substr($this->buffer, strlen($this->buffer) - strlen($prompt))) == $prompt){ 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 * * @param string $command Character to check * @return boolean */ 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); } } ?>