PHP Telnet/SSH 动态登录
Posted
技术标签:
【中文标题】PHP Telnet/SSH 动态登录【英文标题】:PHP Telnet/SSH dynamic login 【发布时间】:2015-09-07 09:19:39 【问题描述】:我有一个困扰我的问题。
我正在尝试自动化 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>";
更新:
我采用的解决方案是 while/switch 循环。我本来会走 Expect 路线,但在将 Expect 模块集成到我的服务器(Windows 机器)上的 PHP 中时,我一直遇到问题。如果我一直在使用 Unix/Linux 服务器,Expect 将是实现这一目标的最简单方法. 我现在只是把它变成了一个工作演示,所以案例陈述中仍然缺少很多变化,错误处理仍然需要弄清楚,但基本的想法就在那里。我仍然想多移动 preg_match 语句以在 while 循环的顶部进行匹配(所以我不会用不同的 preg_match 行向整个案例部分发送垃圾邮件),但这可能比我做的更多暂时想要。希望这可以帮助其他人尝试做同样的事情!
<?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";
?>
【问题讨论】:
if
-else
块和switch
-case
-块几乎相同。
【参考方案1】:
使用expect file_wrapper 有一些复杂性。如果是我,我会为 telnet 建立一个简单的套接字连接,如果 ssh 连接失败,我会轮询提示(超时)。
不经意间,telnet client here 的编写似乎很合理——稍加重命名就可以提供与 ssh2 客户端扩展相同的接口(除了连接位)。
【讨论】:
【参考方案2】:If 语句可能会使您的代码变得不可读。 在这种情况下,我建议您使用 switch-case 块, 因为 switch case 可以让你编写更清晰的代码,并且可以让你更有效地捕获异常值。 在 php 中使用 Expect 很简单:
<?php>
ini_set("expect.loguser", "Off");
$stream = fopen("expect://ssh root@remotehost 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);
?>
【讨论】:
上面的代码看起来很优雅,但是您正在编写一个以只读方式打开的流。读写需要proc_open来调用runtime吗? 确实如此。 proc_open — 执行命令并打开输入/输出的文件指针(取自:php.net/manual/en/function.proc-open.php)以上是关于PHP Telnet/SSH 动态登录的主要内容,如果未能解决你的问题,请参考以下文章
使用PHP中的ajax做登录页面验证用户名是否可用动态调用数据库