user3733560
user3733560

Reputation: 13

Python telnetlib and console connection cisco node

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:

[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 ...

#! /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()]

PS. I stopped the running code by hitting 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:

[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:

[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.

Anyone out there experienced something simular ?

Upvotes: 1

Views: 5375

Answers (3)

Lohmar ASHAR
Lohmar ASHAR

Reputation: 1771

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:

 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.

    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.

        """

Upvotes: 0

user3733560
user3733560

Reputation: 13

I found another way to do it (thanks to 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.

#! /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

Upvotes: 0

Serge Ballesta
Serge Ballesta

Reputation: 149125

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.

Could it be an option to use a simple socket ?

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

Upvotes: 0

Related Questions