0

我正在为串行设备服务器上的嵌入式 Linux 系统开发 C 语言应用程序。我需要访问系统自己的 shell,你可以通过 telnet 访问它,并且有一堆可以在 shell 提示符下执行的自定义命令行界面命令。

一位为设备制造商工作的工程师说,以编程方式发出这些命令行界面命令的唯一方法是通过登录到 shell 的 telnet 客户端。

所以,在我的应用程序中,我想让设备服务器远程登录到它自己,这样它就可以通过它的外壳向自己发出一些命令。在我执行 C 实现之前,我想我只是尝试通过 IRB 打开一个套接字并尝试首先让命令以这种方式工作。实际的 telnet 会话如下所示:

login: admin
password: password

prompt# set watchdog off
prompt# save
Save config to flash ROM y/n
y    

下面是我在 IRB 中运行的简单脚本,以查看是否可以通过 TCP 套接字模拟上述 telnet 会话,因为这就是我将在 Linux 上的 C 中实现的方式:

require 'socket'
@ip = 'xxx.xxx.xxx.xxx'
@enter = "\n"
@sock = TCPSocket.open(@ip,23)
@ret = @sock.write("admin" + @enter)
puts @ret.to_s
sleep(1)      
@ret = @sock.write("password" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(2)
@ret = @sock.write("set watchdog off" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(1)
@ret = @sock.write("save" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(1)
@ret = @sock.write("y")
puts @ret.to_s
puts @sock.read(1)
sleep(1)

@sock.close

在 C 中打开、写入和读取套接字对我来说没有问题,而且我已经在我的应用程序中做了很多,所以我已经有了简单的函数来做这些事情。我的问题是我应该向套接字写入哪些字符串来模拟假设在我在提示符下键入内容后按下回车键?"\n" 或 "\x0a" 不工作。或者我可以使用一个 linux C 库来让这种方式对我来说更容易吗?

4

2 回答 2

3

您绝对可以阅读 telnet 协议,但请查看下面链接底部的答案,其中涉及使用 WireShark 实际查看来回飞行的内容。

http://www.codeproject.com/Questions/147555/Telnet-client-using-TCP

如果您从未使用过数据包嗅探器,那么它非常有启发性(就像对我一样)。以下是我通过 TCP 套接字连接进行 telnet 的步骤。

(1) 按照上述链接中的说明捕获网络上的流量,同时准确执行您希望使用所需应用程序/脚本最终执行的命令,从创建 telnet 连接开始,到关闭它结束

(2) 停止捕获会话后,您可能会在捕获的过程中看到同时在您的网络上发生的许多其他流量,因此请使用 ip.addr == xxx.xxx.xxx.xxx 将其过滤掉,其中你当然使用远程计算机的IP地址

(3) 现在,当您查看在您的计算机和远程计算机之间来回传输的所有内容时,您可以看到应用程序如何为每条消息分离出不同的通信层。您要特别注意带有“Telnet”协议的消息,这意味着它是最高层。当您在 WireShark 窗口的最顶部窗格中突出显示一条消息时,您会在第二个窗格中看到它如何将该消息分成不同的层。在第二个窗格中,如果您突出显示 Telnet 层,您可以看到在第三个窗格中,实现 telnet 协议的消息字节如何被突出显示。花一些时间来分析回传的消息。你可以更直接地看到,

(4) 现在您只想在应用程序中基本模拟您可以在 Wireshark 中看到的通信。请记住,由于您在应用程序中打开 TCP 套接字,因此您的应用程序在幕后处理所有 TCP 和下行通信内容,因此您只需担心将 telnet 字节/数据/命令读/写到 TCP 套接字.

这种方法根本不需要很长时间,并且不需要在您的系统上添加任何额外的东西,如果您正在处理系统资源有限/非常简单的操作系统/根本没有操作系统的设备,这非常棒。例如,我设备上的 mini Linux 系统没有安装 Expect,也没有简单的安装方法。我的意思是它可以做到,但我们在这里谈论非常烦人的东西。此外,即使在盒子上安装东西的方式不太疯狂,我已经有 50 个在我已经设置为自动更新到我肯定放在服务器上的任何新二进制文件的字段中,但他们并不期望还必须从我的服务器下载脚本。同样,他们没有安装允许他们运行该脚本的操作系统。所以我们在这里谈论的是相对较低级别的东西,保持简单不是一种选择,而是一种要求。我的最终 C 函数让我的串行设备服务器 telnet 到自己(访问自己的命令行界面的唯一方法——这是制造商的选择)并向自己发出必要的命令,如下所示:

turnoff_watchdog
{
  // telnetsock, TCP socket to 127.0.0.1 at port 23, has already been set previously

  char negotiation_1[] = {0xFF,0xFC,0X18,0xFF,0xFC,0x20,0xFF,0xFC,0x23,0xFF,0xFC,0x27,0xFF,0xFD,0x03};
  char negotiation_2[] = {0xFF,0xFC,0x01,0xFF,0xFC,0x1F,0xFF,0xFE,0x05,0xFF,0xFB,0x21};
  char telnet_login_name[] = "admin\r\n";
  char telnet_login_password[] = "password\r\n";
  char set_command[] = "set watchdog off\r\n";
  char save_command[] = "save\r\n";
  char confirm[] = "y\n";
  char escape[] = {0x5E,0x5D,0x0A}; // ^]
  char quit[] = "quit\n";

  sleep(2);
  write(telnetsock,&negotiation_1[0],15);
  sleep(2);
  write(telnetsock,&negotiation_2[0],12);
  sleep(2);
  write(telnetsock,&telnet_login_name[0],7);
  sleep(2);
  write(telnetsock,&telnet_login_password[0],10);
  sleep(2);
  write(telnetsock,&set_command[0],18);
  sleep(2);
  write(telnetsock,&save_command[0],6);
  sleep(2);
  write(telnetsock,&confirm[0],2);
  sleep(2);
  write(telnetsock,&escape[0],3);
  sleep(2);
  write(telnetsock,&quit[0],5);
  sleep(2);

  return(0);
}

就那么简单。最困难的部分是原始(我使用 Wireshark 捕获的)telnet 协商要长一些,但在查看了正在协商的选项后,我意识到我不需要大部分,所以我砍了大幅下降。顺便说一句,如果你被字节吓倒了,不要害怕。没什么大不了的。你看到如何在negotiation_1negotiation_2 above, 0xFF keeps popping up every 3 bytes? Well all those two negotiations are are strings of commands/negotiations each 3 bytes in length starting with 0xFF, trying to communicate what it will and won't do during the telnet session. And the second byte in each chunk of 3 bytes (either 0xFB, 0xFC, 0xFD or 0xFE) is just communicating whether it WILL or WON'T do something or whether it wants the other guy to DO or DON'T something. The last byte is just the option each is willing or not willing to do or going to do or not do. You'll see it in Wireshark how it all gets broken down. It's actually pretty funny the language of it. I didn't include the lines I originally had where I was reading the socket to see the responses to the negotiations (which I had done all that testing and stuff in ruby since I could just run it in irb super easy), but you must do that and basically just negotiate as little as you can until you get sent the login prompt and then as you can see it's super straightforward. Make sure in testing to determine the first negotiation you will write to the socket, that when you open the socket, you read in whatever telnet control sequences are sent FIRST. Then you only want to respond to those sequences--mostly in the negative to reduce the amount of negotiation. Have fun!

于 2012-10-02T14:28:10.723 回答
0

你问了一个 C 问题但你发布了脚本,如果你想知道 telnet 是如何工作的,请阅读 RFC。在这种情况下 854

https://www.rfc-editor.org/rfc/rfc854

于 2012-10-02T03:05:34.577 回答