3

I would like to understand how to use telnetlib3 for a simple scenario.

The longstanding telnetlib (not 3) has a simple example at https://docs.python.org/3/library/telnetlib.html where the python program connects to a telnet server, then looks for prompts and provides responses. One can readily see how to extend this example to different prompts, add timeouts, and add more prompt-response steps.

import getpass
import telnetlib

HOST = "localhost"
user = input("Enter your remote account: ")
password = getpass.getpass()

tn = telnetlib.Telnet(HOST)

tn.read_until(b"login: ")
tn.write(user.encode('ascii') + b"\n")
if password:
    tn.read_until(b"Password: ")
    tn.write(password.encode('ascii') + b"\n")

tn.write(b"ls\n")
tn.write(b"exit\n")

print(tn.read_all().decode('ascii'))

However, telnetlib (not 3) is deprecated.

The replacement, telnetlib3 ( https://telnetlib3.readthedocs.io/en/latest/intro.html#quick-example ) provides an example based on asyncio, and the async "shell" function (that interacts with the server) blocks waiting for prompt (rationale for async) and always responds to the server with 'y'.

import asyncio, telnetlib3

async def shell(reader, writer):
    while True:
        # read stream until '?' mark is found
        outp = await reader.read(1024)
        if not outp:
            # End of File
            break
        elif '?' in outp:
            # reply all questions with 'y'.
            writer.write('y')

        # display all server output
        print(outp, flush=True)

    # EOF
    print()

loop = asyncio.get_event_loop()
coro = telnetlib3.open_connection('localhost', 6023, shell=shell)
reader, writer = loop.run_until_complete(coro)
loop.run_until_complete(writer.protocol.waiter_closed)

I am a few clues short on how to get code that's structured this way to perform the more mainstream task that's demonstrated in the (literally!) straightforward telnetlib (not 3) example where the server provides a series of different prompts, and the python program is to provide corresponding responses. I suspect that this is partly down to my unfamiliarity with asyncio and what code patterns one should use to get an async function to carry out a series of steps.

So it would be a great help to see the telnetlib (not 3) example implemented in this style.

gwideman
  • 2,705
  • 1
  • 24
  • 43

2 Answers2

4

I think it's a bit of a stretch to call telnetlib3 a "replacement" for telnetlib. I guess it's similar in that it allows you to write a telnet client (or server), but it's really an entirely different beast. For the sort of thing you're doing in the initial telnetlib example, I would generally reach for pexpect (or just, you know, normal expect).

It looks like PEP 594 also points to Exscript as a solution, and that looks closer to telnetlib than telnetlib3.


Here's an example I whipped up that uses telnetlib3 to connect to a local switch, login, send the enable command, and then log out:

import asyncio
import telnetlib3

async def shell(reader, writer):
    rules = [
            ('User:', 'admin'),
            ('Password:', 'secret'),
            (') >', 'enable'),
            (') #', 'exit'),
            (') >', 'logout'),
            ]

    ruleiter = iter(rules)
    expect, send = next(ruleiter)
    while True:
        outp = await reader.read(1024)
        if not outp:
            break

        if expect in outp:
            writer.write(send)
            writer.write('\r\n')
            try:
                expect, send = next(ruleiter)
            except StopIteration:
                break

        # display all server output
        print(outp, flush=True)

    # EOF
    print()

async def main():
    reader, writer = await telnetlib3.open_connection('office-sw-0', 23, shell=shell)
    await writer.protocol.waiter_closed


if __name__ == '__main__':
    asyncio.run(main())

I don't really like it. I'd much rather do something like this:

import sys
import pexpect

rules = [
    ("User:", "admin"),
    ("Password:", "secret"),
    (r"\) >", "enable"),
    (r"\) #", "exit"),
    (r"\) >", "logout"),
    (pexpect.EOF, None),
]

client = pexpect.spawn("telnet office-sw-0")

# This is so we can see what's happening.
client.logfile = sys.stdout.buffer

for expect, send in rules:
    client.expect(expect)
    if send is None:
        break
    client.sendline(send)
larsks
  • 277,717
  • 41
  • 399
  • 399
  • 2
    Thanks larsks for your answer. I had not looked closely at Exscript because its docs show a requirement for Python 2.7 (I use 3.x) and in any case it looked way to elaborate for my needs. Also, no documentation of any of the API, though there are comments in the source code, it turns out. But now I see Exscript may also support Python 3.x. And notably it includes a module telnetlib.py, which looks to be closely derived from old telnetlib.py, and contains some comments to that effect. Not sure what to make of that, and whether using just that module by itself is an option. – gwideman Mar 19 '23 at 12:25
  • 1
    Thanks also for your asyncio and pexpect examples. Your asyncio example goes in the direction that I though might be required, but disbelieved because it would carry out the interaction with the server sort of "adrift" from the main program. I'm going to have to study asyncio to more thoroughly understand how multiple tasks are supposed to communicate and coordinate with each other. Meanwhile your pexpect example I see gets the job done, but at the cost of invoking an external program, and not in a cross-platform way. But good to know all the same! – gwideman Mar 19 '23 at 23:23
  • exscript does not seem to be maintained, and pexpect does not work as telnetlib3 – Alex Jul 13 '23 at 09:53
2

For the simple action and response application, you can code using telnetlib3 no differently from telnetlib (withou 3) and pexpect besides a few async key words. The following is a complete and working example to send an email via telnet. Hope this helps, you are welcome.

import asyncio
import re
import telnetlib3

rules = [
    ("220 .* SMTP", "HELO localhost"),
    ("250 Hello and welcome", "MAIL FROM:<from_email_address"),
    ("250 Ok", "RCPT TO:<to_email_address>"),
    ("250 Ok", "DATA"),
    ("354 Proceed", "<email header and body>"),
    ("250 Ok", "QUIT")
]

async def main():
    reader, writer = await telnetlib3.open_connection(host, port)

    for prompt, action in rules:
        response = await reader.readline()
        print(response.strip())

        if re.match(prompt, response):
            writer.write(action + "\n")
            print(action)

asyncio.run(main())
puravidaso
  • 1,013
  • 1
  • 5
  • 22