2015-06-22 97 views
6

我有一个难倒我的问题。PHP Telnet/SSH动态登录

我试图自动化CLI登录到路由器并运行通过网页获得的一些命令。但是我不知道路由器是否启用了telnet或SSH(可能是其中之一,或者两者兼而有之),并且我有一个可能的用户名/密码组合列表,我需要尝试访问。
哦,我无法更改协议类型或设备上的凭据,所以这不是一个真正的选择。

我能弄清楚如何使用已知的协议和登录凭据登录到路由器并运行必要的命令(下面包含),但我不知道是否应该使用if/else块来工作通过telnet/ssh决定,或者如果switch语句可能会更好?在PHP中使用Expect是更简单的方法吗?

function tunnelRun($commands,$user,$pass, $yubi){ 
    $cpeIP = "1.2.3.4"; 
    $commands_explode = explode("\n", $commands); 

    $screenOut = ""; 

    $ssh = new Net_SSH2('router_jumphost'); 
    if (!$ssh->login($user, $pass . $yubi)) { 
     exit('Login Failed'); 
    } 


    $ssh->setTimeout(2); 
    $ssh->write("ssh -l username $cpeIP\n"); 
    $ssh->read("assword:"); 
    $ssh->write("password\n"); 
    $ssh->read("#"); 
    $ssh->write("\n"); 
    $cpePrompt = $ssh->read('/.*[#|>]/', NET_SSH2_READ_REGEX); 
    $cpePrompt = str_replace("\n", '', trim($cpePrompt)); 
    $ssh->write("config t\n"); 


    foreach ($commands_explode as $i) { 
     $ssh->write("$i\n"); // note the "\n" 
     $ssh->setTimeout(2); 
     $screenOut .= $ssh->read(); 

    } 
    $ssh->write("end\n"); 
    $ssh->read($cpePrompt); 
    $ssh->write("exit\n"); 
    echo "Router Update completed! Results below:<br><br>"; 

    echo "<div id=\"text_out\"><textarea style=\" border:none; width: 700px;\" rows=\"20\">".$screenOut."</textarea></div>"; 

更新:

我是一会儿/开关循环去解决方案。我会走的Expect路线,但我一直在遇到在我的服务器上将Expect模块集成到PHP中的问题(Windows框)。如果我一直在使用Unix/Linux服务器,Expect将是实现此目的的最简单方法。 现在我只是把它变成了一个工作演示,所以仍然存在很多从case语句中缺失的变体,并且仍然需要弄清楚错误处理的方式,但是基本的想法就在那里。我仍然希望将preg_match语句稍微移动一些,以便在while循环的顶部进行匹配(所以我不会用不同的preg_match行将整个case部分发送给垃圾邮件),但这可能证明比我更有效现在想要。希望这可能会帮助其他人试图做同样的事!

<?php 
include('Net/SSH2.php'); 
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX); 
ini_set('display_errors', 1); 

$conn = new Net_SSH2('somewhere.outthere.com'); 
if (!$conn->login($user, $pass . $yubi)) { 
    exit('Login Failed'); 
} 

$prompt = "Testing#"; 

$conn->setTimeout(2); 
$conn->write("PS1=\"$prompt\""); 
$conn->read(); 
$conn->write("\n"); 
$screenOut = $conn->read(); 

//echo "$screenOut is set on the shell<br><br>"; 
echo $login_db[3][0]. " ". $login_db[3][1]; 

$logged_in = false; 
$status = "SSH"; 
$status_prev = ""; 
$login_counter = 0; 
while (!$logged_in && $login_counter <=3) { 
    switch ($status) { 

     case "Telnet": 
      break; 
     case "SSH": 
      $conn->write("\n"); 
      $conn->write("ssh -l " . $login_db[$login_counter][0] . " $cpeIP\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/Permission denied.*/', $status) ? true : false): 
      $conn->write(chr(3)); //Sends Ctrl+C 
      $status = $conn->read(); 
      if (strstr($status, "Testing#")) { 
       $status = "SSH"; 
       $login_counter++; 
       break; 
      } else { 
       break 2; 
      } 
     case (preg_match('/[pP]assword:/', $status) ? true : false): 

      $conn->write($login_db[$login_counter][1] . "\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/yes\/no/', $status) ? true : false): 
      $conn->write("yes\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/(^[a-zA-Z0-9_]+[#]$)|(>)/', $status,$matches) ? true : false): 


      $conn->write("show version\n"); 
      $status = $conn->read(">"); 
      if(preg_match('/ADTRAN|Adtran|Cisco/', $status)? true:false){ 
       $logged_in = true; 
       break; 
      } 

     default: 
      echo "<br>Something done messed up! Exiting"; 
      break 2; 

    } 
    //echo "<pre>" . $conn->getLog() . "</pre>"; 
} 
if ($logged_in === true) { 
    echo "<br> Made it out of the While loop cleanly"; 
} else { 
    echo "<br> Made it out of the While loop, but not cleanly"; 
} 
echo "<pre>" . $conn->getLog() . "</pre>"; 

$conn->disconnect(); 
echo "disconnected cleanly"; 
} 
?> 
+0

'if' -'else'块和'switch'''case'-blocks实际上是相同的。 –

回答

3

如果语句可能使您的代码变得不可读。
在这种情况下,我建议你使用开关箱块,
因为开关柜可以让你编写更清晰的代码,并且可以让你更有效地捕获异常值。

使用在PHP期望很简单:

<?php> 
ini_set("expect.loguser", "Off"); 

$stream = fopen("expect://ssh [email protected] uptime", "r"); 

$cases = array (
    array (0 => "password:", 1 => PASSWORD) 
); 

switch (expect_expectl ($stream, $cases)) { 
    case PASSWORD: 
     fwrite ($stream, "password\n"); 
     break; 
    default: 
     die ("Error was occurred while connecting to the remote host!\n"); 
} 

while ($line = fgets($stream)) { 
     print $line; 
} 
fclose ($stream); 

?> 
+0

上面的代码看起来非常优雅,但是您正在编写一个只读的流。读取和写入需要proc_open来确保调用运行时? – symcbean

+0

确实。 proc_open - 执行命令并打开输入/输出文件指针 (取自:http://php.net/manual/en/function.proc-open.php) –

0

有一些并发症使用期望file_wrapper。如果是我,我只需要为telnet提供一个简单的套接字连接,并在ssh连接失败时轮询提示(超时)。

在一次不定期的检查中,telnet client here似乎是明智的写入 - 并且有一点重命名可以提供与ssh2客户端扩展相同的接口(除了连接位之外)。