Python telnetlib和控制台连接cisco节点。

时间:2022-02-23 22:25:48

I am trying to connect to the console connection of a cisco node via a cisco terminal/comm server. To do this I telnet to the IP-address of the cisco terminal/comm server on a specific port let us say X.X.X.X - port 2068. When I am doing this from my computer via the CLI it looks like this:

我试图通过cisco终端/comm服务器连接到cisco节点的控制台连接。要做到这一点,我telnet到cisco终端/comm服务器在一个特定端口上的ip地址,让我们说x.x.x。X - 2068端口。当我从我的电脑通过CLI做这个的时候它看起来是这样的:

[user@computer]$ telnet X.X.X.X 2068
Trying X.X.X.X...
Connected to X.X.X.X (X.X.X.X).
Escape character is '^]'.

Username: <user>
Password: 

console-cisco-node>

So no problem via the CLI on my computer. But when I run the below Python code on my computer it does not seem to work ...

我的电脑上的CLI没有问题。但是当我在我的电脑上运行下面的Python代码时,它似乎并不起作用。

#! /usr/bin/env python

import telnetlib

tn = telnetlib.Telnet("X.X.X.X",2068)
tn.set_debuglevel(8)
data = tn.read_some()
tn.close()
if data == '':
  print 'variable data is EMPTY'
else:
  print data
  print "variable data is FILLED !!!"

When I run this code I only get to see this, it looks like the 'tn.read_some()' just waits forever because nothing is coming from the cisco terminal/comm server ? [same for tn.read_all()]

当我运行这段代码时,我只看到这个,它看起来像“tn.read_some()”,因为没有任何东西来自cisco终端/comm服务器?(相同tn.read_all())

PS. I stopped the running code by hitting CTRL-C

我通过按CTRL-C停止运行代码。

[user@computer]$ ./test.py   
Telnet(X.X.X.X,2068): recv '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
Telnet(X.X.X.X,2068): IAC WILL 1
Telnet(X.X.X.X,2068): IAC WILL 3
Telnet(X.X.X.X,2068): IAC DO 24
Telnet(X.X.X.X,2068): IAC DO 31
Telnet(X.X.X.X,2068): recv '\xff\xfc\x01'
Telnet(X.X.X.X,2068): IAC WONT 1
Telnet(X.X.X.X,2068): recv '\xff\xfc\x03'
Telnet(X.X.X.X,2068): IAC WONT 3
Telnet(X.X.X.X,2068): recv '\xff\xfe\x18'
Telnet(X.X.X.X,2068): IAC DONT 24
Telnet(X.X.X.X,2068): recv '\xff\xfe\x1f'
Telnet(X.X.X.X,2068): IAC DONT 31

Traceback (most recent call last):
  File "./test.py", line 7, in ?
    data = tn.read_some()
  File "/usr/lib64/python2.4/telnetlib.py", line 345, in read_some
    self.fill_rawq()
  File "/usr/lib64/python2.4/telnetlib.py", line 521, in fill_rawq
    buf = self.sock.recv(50)
KeyboardInterrupt

When I change the 'tn.read_some()' in the code to 'tn.read_eager()' or 'tn.read_very_eager()' or 'tn.read_lazy()' or 'tn.read_very_lazy()' and run the code again it shows me this:

当我将代码中的“tn.read_some()”更改为“tn.read_eager()”或“tn.read_very_eager()”或“tn.read_lazy()”或“tn.read_lazy()”,然后再运行代码时,它会告诉我:

[user@computer]$ ./test.py
variable data is EMPTY

When I change the python code to connect not to the console connection of the cisco node but to the management connection of the cisco node (another IP-address Y.Y.Y.Y on normal port 23) as follows it works just fine, I see this output:

当我更改python代码时,将不连接到cisco节点的控制台连接,而是连接到cisco节点的管理连接(另一个ip地址。Y在正常端口23)如下图所示,我看到输出:

[user@computer]$ ./test1.py 
Telnet(Y.Y.Y.Y,23): recv '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
Telnet(Y.Y.Y.Y,23): IAC WILL 1
Telnet(Y.Y.Y.Y,23): IAC WILL 3
Telnet(Y.Y.Y.Y,23): IAC DO 24
Telnet(Y.Y.Y.Y,23): IAC DO 31
Telnet(Y.Y.Y.Y,23): recv '\r\n************************************************'

************************************************
variable data is FILLED !!!

So the Python code is OK I think. It is just that the cisco terminal/COMM server (X.X.X.X) is reacting in such a different way then normal that Python telnetlib is confused I think.

所以我认为Python代码是可以的。只是cisco终端/COMM服务器(X.X.X.X)以一种不同的方式做出反应,而Python telnetlib的正常情况是我想的。

Anyone out there experienced something simular ?

有没有人有过类似的经历?

3 个解决方案

#1


0  

It looks like on port 2068 you do not have a true telnet protocol. From telnet man When connecting to a non-standard port, telnet omits any automatic initiation of TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done.. But telnetlib supposes that if you use it, you want a telnet protocol. As you said it is not easy to use wireshark on the connection, you could try to do CLI telnet to port -2068 to verify that you get same problem than with telnetlib.

在端口2068上,你没有一个真正的telnet协议。从telnet man连接到非标准端口,telnet omits自动启动telnet选项。当端口号前面有一个负号时,初始的选项协商就完成了。但是telnetlib假设,如果您使用它,您需要一个telnet协议。正如您所说的,在连接上使用wireshark是不容易的,您可以尝试使用CLI telnet到端口-2068,以验证您的问题与telnetlib相同。

Could it be an option to use a simple socket ?

是否可以选择使用简单的套接字?

s = socket.create_connection(('X.X.X.X', 2068))
data = s.recv(1024)
...

#2


0  

I found another way to do it (thanks to Serge Ballesta)

我找到了另一种方法(多亏了Serge Ballesta)

The telnetlib object in my case was not possible so i had to use the socket object to connect to the console connection of a cisco node via a cisco terminal/comm server. Below the raw basic code which gives me the possibility to start write the program I need.

在我的情况下,telnetlib对象是不可能的,因此我必须使用socket对象通过cisco终端/comm服务器连接到cisco节点的控制台连接。下面是原始的基本代码,它让我有可能开始编写我需要的程序。

#! /usr/bin/env python

import socket
import time

try:
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.connect(("X.X.X.X",2068))
   data1 = s.recv(1024)
   #
   # when the connection with the server is made the server sends some (IAC) commands
   #
   #    '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
   #
   # as far as I know this means the following:
   #
   #     IAC WILL ECHO
   #     IAC WILL SUPRESS GO AHEAD
   #     IAC DO   TERMINAL TYPE
   #     IAC DO   WINDOW SIZE
   #
   # WILL means the server wants to use the indicated facility if the client has the possibilty to use this facility
   # (client can reply with DO or DONT)
   #
   # DO means the server has the possibility for the indicated facility and asks the client if he wants to use this facility
   # (client can reply with WILL or WONT)
   #
   #
   # PS.
   #      WILL = xFB = 251
   #      WONT = xFC = 252
   #      DO   = xFD = 253
   #      DONT = xFE = 254
   #
   #
   #
   # Python is going to answer as follwos:
   #
   #     IAC DONT ECHO
   #     IAC DO   SUPRESS GO AHEAD
   #     IAC WONT TERMINAL TYPE
   #     IAC WONT WINDOW SIZE
   #

   s.sendall('\xff\xfe\x01\xff\xfd\x03\xff\xfc\x18\xff\xfc\x1f')

   data2 = s.recv(1024) # server sends '\xff\xfc\x01' = IAC WONT ECHO as a reply back

   # send an enter to make the hop from the terminal server to the console connection of the node we want to be on
   s.send('\r')

   data3 = ''
   while not 'Username: ' in data3:
      data3 = s.recv(2048)

   s.send('<USERNAME>\r')
   time.sleep(0.1)

   s.recv(1024)
   s.send('<PASSWORD>\r')
   time.sleep(0.1)
   data5 = s.recv(1024)
   s.send('exit\r')

   s.close()

   #print repr(data1)   # '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f' from the server
   #print repr(data2)   # '\xff\xfc\x01' from the server
   #print data3         # banner and Username: from the console connection of the node we want to be on  

   prompt_console = data5.split()[-1]
   print 'prompt van de console = %s' %prompt_console[:len(prompt_console)-1]

except socket.error, e:
   print e

except socket.timeout, e:
   print e

#3


0  

I also interact with Cisco(and other routers). For the telnet communication I've extended the Telnet class and added a read method like this:

我还与Cisco(以及其他路由器)进行交互。对于telnet通信,我扩展了telnet类并添加了如下的读取方法:

 def read(self, timeout):
    s = time.time()
    resp = ""
    while True:
        res = self.read_eager()
        if not res and resp:
            return resp
        resp += res
        if time.time() - s >= timeout:
            return resp
        time.sleep(0.001)

so basically the "problem" is that the various read_* methods are not giving "all", basically because there is no definition of "all", it will return whatever they have in the "buffer", or they'll fill the buffer then return the content, depending on which method you'r calling. My approach was "read while there's still something to read (stop when I read "") or until it passes more than timeout seconds. Also you should look into the read_until method works pretty much as expect does.

基本上,“问题”是不同的read_*方法没有给出“全部”,主要是因为没有“all”的定义,它将返回“缓冲区”中的任何内容,否则它们将填充缓冲区,然后返回内容,这取决于您调用的方法。我的方法是“阅读时仍然有一些东西可以读(当我读的时候停止),或者直到它超过超时时间。”同样,您应该查看read_until方法,这与expect所做的工作非常相似。

    def read_until(self, match, timeout=None):
        """Read until a given string is encountered or until timeout.

        When no match is found, return whatever is available instead,
        possibly the empty string.  Raise EOFError if the connection
        is closed and no cooked data is available.

        """

#1


0  

It looks like on port 2068 you do not have a true telnet protocol. From telnet man When connecting to a non-standard port, telnet omits any automatic initiation of TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done.. But telnetlib supposes that if you use it, you want a telnet protocol. As you said it is not easy to use wireshark on the connection, you could try to do CLI telnet to port -2068 to verify that you get same problem than with telnetlib.

在端口2068上,你没有一个真正的telnet协议。从telnet man连接到非标准端口,telnet omits自动启动telnet选项。当端口号前面有一个负号时,初始的选项协商就完成了。但是telnetlib假设,如果您使用它,您需要一个telnet协议。正如您所说的,在连接上使用wireshark是不容易的,您可以尝试使用CLI telnet到端口-2068,以验证您的问题与telnetlib相同。

Could it be an option to use a simple socket ?

是否可以选择使用简单的套接字?

s = socket.create_connection(('X.X.X.X', 2068))
data = s.recv(1024)
...

#2


0  

I found another way to do it (thanks to Serge Ballesta)

我找到了另一种方法(多亏了Serge Ballesta)

The telnetlib object in my case was not possible so i had to use the socket object to connect to the console connection of a cisco node via a cisco terminal/comm server. Below the raw basic code which gives me the possibility to start write the program I need.

在我的情况下,telnetlib对象是不可能的,因此我必须使用socket对象通过cisco终端/comm服务器连接到cisco节点的控制台连接。下面是原始的基本代码,它让我有可能开始编写我需要的程序。

#! /usr/bin/env python

import socket
import time

try:
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.connect(("X.X.X.X",2068))
   data1 = s.recv(1024)
   #
   # when the connection with the server is made the server sends some (IAC) commands
   #
   #    '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
   #
   # as far as I know this means the following:
   #
   #     IAC WILL ECHO
   #     IAC WILL SUPRESS GO AHEAD
   #     IAC DO   TERMINAL TYPE
   #     IAC DO   WINDOW SIZE
   #
   # WILL means the server wants to use the indicated facility if the client has the possibilty to use this facility
   # (client can reply with DO or DONT)
   #
   # DO means the server has the possibility for the indicated facility and asks the client if he wants to use this facility
   # (client can reply with WILL or WONT)
   #
   #
   # PS.
   #      WILL = xFB = 251
   #      WONT = xFC = 252
   #      DO   = xFD = 253
   #      DONT = xFE = 254
   #
   #
   #
   # Python is going to answer as follwos:
   #
   #     IAC DONT ECHO
   #     IAC DO   SUPRESS GO AHEAD
   #     IAC WONT TERMINAL TYPE
   #     IAC WONT WINDOW SIZE
   #

   s.sendall('\xff\xfe\x01\xff\xfd\x03\xff\xfc\x18\xff\xfc\x1f')

   data2 = s.recv(1024) # server sends '\xff\xfc\x01' = IAC WONT ECHO as a reply back

   # send an enter to make the hop from the terminal server to the console connection of the node we want to be on
   s.send('\r')

   data3 = ''
   while not 'Username: ' in data3:
      data3 = s.recv(2048)

   s.send('<USERNAME>\r')
   time.sleep(0.1)

   s.recv(1024)
   s.send('<PASSWORD>\r')
   time.sleep(0.1)
   data5 = s.recv(1024)
   s.send('exit\r')

   s.close()

   #print repr(data1)   # '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f' from the server
   #print repr(data2)   # '\xff\xfc\x01' from the server
   #print data3         # banner and Username: from the console connection of the node we want to be on  

   prompt_console = data5.split()[-1]
   print 'prompt van de console = %s' %prompt_console[:len(prompt_console)-1]

except socket.error, e:
   print e

except socket.timeout, e:
   print e

#3


0  

I also interact with Cisco(and other routers). For the telnet communication I've extended the Telnet class and added a read method like this:

我还与Cisco(以及其他路由器)进行交互。对于telnet通信,我扩展了telnet类并添加了如下的读取方法:

 def read(self, timeout):
    s = time.time()
    resp = ""
    while True:
        res = self.read_eager()
        if not res and resp:
            return resp
        resp += res
        if time.time() - s >= timeout:
            return resp
        time.sleep(0.001)

so basically the "problem" is that the various read_* methods are not giving "all", basically because there is no definition of "all", it will return whatever they have in the "buffer", or they'll fill the buffer then return the content, depending on which method you'r calling. My approach was "read while there's still something to read (stop when I read "") or until it passes more than timeout seconds. Also you should look into the read_until method works pretty much as expect does.

基本上,“问题”是不同的read_*方法没有给出“全部”,主要是因为没有“all”的定义,它将返回“缓冲区”中的任何内容,否则它们将填充缓冲区,然后返回内容,这取决于您调用的方法。我的方法是“阅读时仍然有一些东西可以读(当我读的时候停止),或者直到它超过超时时间。”同样,您应该查看read_until方法,这与expect所做的工作非常相似。

    def read_until(self, match, timeout=None):
        """Read until a given string is encountered or until timeout.

        When no match is found, return whatever is available instead,
        possibly the empty string.  Raise EOFError if the connection
        is closed and no cooked data is available.

        """