如何以编程方式telnet并通过TCP套接字发出命令?

时间:2012-10-01 22:16:47

标签: c sockets telnet

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

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

因此,在我的应用程序中,我希望设备服务器telnet自身,以便它可以通过它的shell向自己发出一些命令。在我执行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库,这对我来说更容易吗?

2 个答案:

答案 0 :(得分:2)

你绝对可以阅读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协议的消息的字节。花一些时间来分析正在发回的消息。您可以更直接地看到telnet通信开始时涉及的协商过程。

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

这个方法根本不需要很长时间,并且不需要系统上任何额外的东西,如果你正在处理系统资源有限的设备/非常简单的操作系统/无操作系统,这是很好的。例如,我的设备上的迷你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中,0xFF如何每3个字节弹出一次?那么这两个谈判都是命令/协商的串,每个3字节长度从0xFF开始,尝试传达它在telnet会话期间将会做什么和不会做什么。并且每个3字节块(0xFB,0xFC,0xFD或0xFE)中的第二个字节只是在沟通是否会做或不做某事或者是否希望其他人做或不做某事。最后一个字节只是每个人愿意或不愿意做或不做或不做的选择。你会在Wireshark中看到它如何分解。它的语言实际上非常有趣。我没有包括我最初在读取套接字的地方的行,以查看对协商的响应(我已经完成了所有测试和ruby中的东西,因为我可以在irb中轻松地运行它),但是你必须这样做,基本上只是尽可能少地协商,直到你收到登录提示,然后你可以看到它超级直接。确保在测试中确定您将写入套接字的第一个协商,当您打开套接字时,您将读取所有telnet控制序列首先发送的内容。然后你只想回应那些序列 - 主要是否定的,以减少谈判的数量。玩得开心!

答案 1 :(得分:0)

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

http://tools.ietf.org/html/rfc854