Chapter 16 ■ telnet and SSh
302
There are a few odds and ends in the Telnet object that I need not cover here. You will find them in the telnetlib
Standard Library documentation, including an interact() method that lets the user “talk” directly over your Telnet
connection using the terminal! This kind of call was popular back in the old days when you wanted to automate login
but then take control and issue normal commands yourself.
The Telnet protocol does have a convention for embedding control information, and telnetlib follows these
protocol rules carefully to keep your data separate from any control codes that appear. Thus, you can use a Telnet
object to send and receive all of the binary data you want and ignore the fact that control codes might be arriving as
well. However, if you are doing a sophisticated Telnet-based project, then you might need to process options.
Normally, each time a Telnet server sends an option request, telnetlib flatly refuses to send or receive that
option. However, you can provide a Telnet object with your own callback function for processing options. A modest
example is shown in Listing 16-3. For most options, it simply reimplements the default telnetlib behavior and
refuses to handle any options. (Always remember to respond to each option one way or another; failing to do so will
often hang the Telnet session as the server waits forever for your reply.) If the server expresses interest in the “terminal
type” option, then this client sends a reply of mypython, which the shell command it runs after logging in then sees as
its $TERM environment variable.
Listing 16-3. How to Process Telnet Option Codes
#!/usr/bin/env python3
Foundations of Python Network Programming, Third Edition
https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/telnet_codes.py
How your code might look if you intercept Telnet options yourself
import argparse, getpass
from telnetlib import Telnet, IAC, DO, DONT, WILL, WONT, SB, SE, TTYPE
def process_option(tsocket, command, option):
if command == DO and option == TTYPE:
tsocket.sendall(IAC + WILL + TTYPE)
print('Sending terminal type "mypython"')
tsocket.sendall(IAC + SB + TTYPE + b'\0' + b'mypython' + IAC + SE)
elif command in (DO, DONT):
print('Will not', ord(option))
tsocket.sendall(IAC + WONT + option)
elif command in (WILL, WONT):
print('Do not', ord(option))
tsocket.sendall(IAC + DONT + option)
def main(hostname, username, password):
t = Telnet(hostname)
t.set_debuglevel(1) # uncomment to get debug messages
t.set_option_negotiation_callback(process_option)
t.read_until(b'login:', 10)
t.write(username.encode('utf-8') + b'\r')
t.read_until(b'password:', 10) # first letter might be 'p' or 'P'
t.write(password.encode('utf-8') + b'\r')
n, match, previous_text = t.expect([br'Login incorrect', br'\$'], 10)
if n == 0:
print("Username and password failed - giving up")
else:
t.write(b'exec echo My terminal type is $TERM\n')
print(t.read_all().decode('ascii'))