2

phpを使用してCiscoスイッチにtelnetで接続し、show interface statusコマンドを実行して結果を取得する必要があります。インターネットで見つけたいくつかのphpクラスを試しましたが、どれもデバイスに接続できませんでした。そこで、自分でスクリプトを書こうとしましたが、同じ問題があり、デバイスに接続できません。

ホストは私にバナーメッセージを送信し、次に。を含む改行を送信しますusername:。ユーザー名をで送信し\r\n、しばらく待ってデータを読み取ろうとしましたが、ホストが改行文字を無視しているように見えます。これは私が得た応答です(応答時にexplode('\ n')):

Array
(
    [0] => % 
    [1] => User Access Verification
    [2] => Username:  timeout expired!
)

パスワードの入力を求められなかったのはなぜですか?telnetヘッダーを送信して試してみましたが、変更はありませんでした。誰か助けてくれませんか?

これが私のコードです

<?
$host = "switchName";
$name = "name";
$pass = "pass";
$port = 23;
$timeOut = 15;
$connected = false;
$skipNullLines = true;
$timeout = 125000;


$header1=chr(0xFF).chr(0xFB).chr(0x1F).chr(0xFF).chr(0xFB).chr(0x20).chr(0xFF).chr(0xFB).chr(0x18).chr(0xFF).chr(0xFB).chr(0x27).chr(0xFF).chr(0xFD).chr(0x01).chr(0xFF).chr(0xFB).chr(0x03).chr(0xFF).chr(0xFD).chr(0x03).chr(0xFF).chr(0xFC).chr(0x23).chr(0xFF).chr(0xFC).chr(0x24).chr(0xFF).chr(0xFA).chr(0x1F).chr(0x00).chr(0x50).chr(0x00).chr(0x18).chr(0xFF).chr(0xF0).chr(0xFF).chr(0xFA).chr(0x20).chr(0x00).chr(0x33).chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0x2C).chr(0x33).chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0xFF).chr(0xF0).chr(0xFF).chr(0xFA).chr(0x27).chr(0x00).chr(0xFF).chr(0xF0).chr(0xFF).chr(0xFA).chr(0x18).chr(0x00).chr(0x41).chr(0x4E).chr(0x53).chr(0x49).chr(0xFF).chr(0xF0);
$header2=chr(0xFF).chr(0xFC).chr(0x01).chr(0xFF).chr(0xFC).chr(0x22).chr(0xFF).chr(0xFE).chr(0x05).chr(0xFF).chr(0xFC).chr(0x21);


function read_string()
{
    global $fw,$host,$skipNullLines; 
    $string = "";

    while( !feof($fw) )
    {
        $read = fgets($fw);

        $string .= $read;

        // Probably prompt, stop reading
        if( strpos($read, ':') !== FALSE || strpos($read, '> (enable)') !== FALSE || strpos($read, $host.'#') !== FALSE)
        { break; }
    }

    $string =  explode("\n", $string);

    // Get rid of null lines
    $ret = array();
    for($i = 0; $i<count($string); $i++)
    {
        if( trim($string[$i]) == '' && $skipNullLines ) continue;

        $ret[] = $string[$i];
    }

    return $ret;
}

function send_string($string, $force=false)
{
    GLOBAL $timeout,$fw;
    $string = trim($string);

    // execute only strings that are preceded by "show" (if not forced)
    if(!$force && strpos($string, 'show ') !== 0) 
    {
        return 1;
    }

    fputs($fw, $string."\r\n");
    echo("SEND:".$string."\r\n");
    usleep($timeout);
}


$fw = fsockopen($host, $port, $errno, $errorstr, $timeOut);    
if($fw == false)
{
    echo("Cant connect");
}
else
{
    echo("Connected<br>");
    $connected = true;

    stream_set_timeout($fw, $timeout);

//  fputs($fw, $header1);
//  usleep($timeout);
//  fputs($fw, $header2);
//  usleep($timeout);
    print_r(read_string());
    send_string("test", true);
    print_r(read_string());
}

fclose($fw);
?>

更新 最初にユーザー名を送信してから読んだ場合、パスワードプロンプトが表示されます。私はそれを理解していません、なぜ私は最初にホストからのメッセージを読んでから私の応答を送ることができないのですか?それが今私に機能する方法(応答を送信してからプロンプトを読む)は意味がありません!(それでも「%Authenticationfailed。」というメッセージイベントが正しいパスワード/名前で表示されます)。

...
$connected = true;
stream_set_timeout($fw, $timeout);
send_string("name", true);
send_string("password", true);
print_r(read_string());
...
4

1 に答える 1

4

さて、何が問題だったのかわかりませんが、「いくつかの」テストの後、私にとってはうまくいくこのクラスを書くことができました。他のtelnetクラスがほとんど同じように機能するのに機能しない理由はわかりません。したがって、誰かが同様の問題を抱えている場合は、これを試すことができます:

class TELNET
{
  private $host;
  private $name;
  private $pass;
  private $port;
  private $connected;
  private $connect_timeout;
  private $stream_timetout;

  private $socket;

  public function TELNET()
  {
    $this->port = 23;
    $this->connected = false;         // connected?
    $this->connect_timeout = 10;      // timeout while asking for connection
    $this->stream_timeout = 380000;   // timeout between I/O operations
  }

  public function __destruct()
  {
    if($this->connected) { fclose($this->socket); }
  }

  // Connects to host
  // @$_host - addres (or hostname) of host
  // @$_user - name of user to log in as
  // $@_pass - password of user
  //
  // Return: TRUE on success, other way function will return error string got by fsockopen()
  public function Connect($_host, $_user, $_pass)
  {
    // If connected successfully
    if( ($this->socket = @fsockopen($_host, $this->port, $errno, $errorstr, $this->connect_timeout)) !== FALSE )
    {
      $this->host = $_host;
      $this->user = $_user;
      $this->pass = $_pass;

      $this->connected = true;

      stream_set_timeout($this->socket, 0, 380000);
      stream_set_blocking($this->socket, 1);

      return true;
    }
    // else if coulnt connect
    else return $errorstr;
  }


  // LogIn to host
  //
  // RETURN: will return true on success, other way returns false
  public function LogIn()
  {
    if(!$this->connected) return false;

    // Send name and password
    $this->SendString($this->user, true);
    $this->SendString($this->pass, true);

    // read answer
    $data = $this->ReadTo(array('#'));

    // did we get the prompt from host?
    if( strtolower(trim($data[count($data)-1])) == strtolower($this->host).'#' ) return true;
    else return false;
  }


  // Function will execute command on host and returns output
  //
  // @$_command - command to be executed, only commands beginning with "show " can be executed, you can change this by adding
  //              "true" (bool type) as the second argument for function SendString($command) inside this function (3rd line)
  //
  function GetOutputOf($_command)
  {
    if(!$this->connected) return false;

    $this->SendString($_command);

    $output = array();
    $work = true;

    //
    // Read whole output
    //
    // read_to( array( STRINGS ) ), STRINGS are meant as possible endings of outputs
    while( $work && $data = $this->ReadTo( array("--More--","#") ) )
    {
      // CHeck wheter we actually did read any data
      $null_data = true;
      foreach($data as $line)
      {
        if(trim($line) != "") {$null_data = false;break;}
      }
      if($null_data) { break;}

      // if device is paging output, send space to get rest
      if( trim($data[count($data)-1]) == '--More--')
      {
        // delete line with prompt (or  "--More--")
        unset($data[count($data)-1]);

        // if second line is blank, delete it
        if( trim($data[1]) == '' ) unset($data[1]);
        // If first line contains send command, delete it
        if( strpos($data[0], $_command)!==FALSE ) unset($data[0]);

        // send space
        fputs($this->socket, " ");
      }

      // ak ma vystup max dva riadky
      // alebo sme uz nacitali prompt
      // IF we got prompt (line ending with #)
      // OR string that we've read has only one line
      //    THEN we reached end of data and stop reading
      if( strpos($data[count($data)-1], '#')!==FALSE /* || (count($data) == 1 && $data[0] == "")*/  )
      {
        // delete line with prompt
        unset($data[count($data)-1]);

        // if second line is blank, delete it
        if( trim($data[1]) == '' ) unset($data[1]);
        // If first line contains send command, delete it
        if( strpos($data[0], $_command)!==FALSE ) unset($data[0]);

        // stop while cyclus
        $work = false;
      }

      // get rid of empty lines at the end
      for($i = count($data)-1; $i>0; $i--)
      {
        if(trim($data[$i]) == "") unset($data[$i]);
        else break;
      }

      // add new data to $output
      foreach($data as $v)
      { $output[] = $v; }
    }

    // return output
    return $output;
  }


  // Read from host until occurence of any index from $array_of_stops
  // @array_of_stops - array that contains strings of texts that may be at the end of output
  // RETURNS: output of command as array of lines
  function ReadTo($array_of_stops)
  {
    $ret = array();
    $max_empty_lines = 3;
    $count_empty_lines = 0;

    while( !feof($this->socket) )
    {
      $read = fgets($this->socket);
      $ret[] = $read;

      //
      // Stop reading after (int)"$max_empty_lines" empty lines
  //
      if(trim($read) == "")
      {
        if($count_empty_lines++ > $max_empty_lines) break;
      }
      else $count_empty_lines = 0;

      //
      // Does last line of readed data contain any of "Stop" strings ??
      $found = false;
      foreach($array_of_stops AS $stop)
      {
        if( strpos($read, $stop) !== FALSE ) { $found = true; break; }
      }
      // If so, stop reading
      if($found) break;
    }

    return $ret;
  }



  // Send string to host
  // If force is set to false (default), function sends to host only strings that begins with "show "
  //
  // @$string - command to be executed
  // @$force - force command? Execute if not preceeded by "show " ?
  // @$newLine - append character of new line at the end of command?
  function SendString($string, $force=false, $newLine=true)
  {
    $t1 = microtime(true);
    $string = trim($string);

    // execute only strings that are preceded by "show"
    // and execute only one command (no new line characters) !
    if(!$force && strpos($string, 'show ') !== 0 && count(explode("\n", $string)) == 1)
    {
      return 1;
    }


    if($newLine) $string .= "\n";
    fputs($this->socket, $string);

    $t2 = microtime(true);
  }

}


// EXAMPLE
$host = "hostname";
$name = "username";
$pass = "password";


$t = new TELNET();
echo("CONNECT:".$t->Connect($host, $name, $pass)."<br>");
echo("LOGIN:".(int)$t->LogIn());
echo("<br>OUTPUT:<br>");
print_r($t->GetOutputOf("show snmp"));
print_r($t->GetOutputOf("show users"));
print_r($t->GetOutputOf("show interface status"));

PS: 私のデバイスのプロンプトは "hostname#" です。そのため、ログイン関数を編集して、このコードがデバイスのプロンプトで機能するようにする必要がある場合があります (そのため、 GetOutputOf() で必要になる場合があります)。

于 2012-05-28T08:59:21.343 に答える