123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- r"""TELNET client class.
- Based on RFC 854: TELNET Protocol Specification, by J. Postel and
- J. Reynolds
- Example:
- >>> from telnetlib import Telnet
- >>> tn = Telnet('www.python.org', 79) # connect to finger port
- >>> tn.write(b'guido\r\n')
- >>> print(tn.read_all())
- Login Name TTY Idle When Where
- guido Guido van Rossum pts/2 <Dec 2 11:10> snag.cnri.reston..
- >>>
- Note that read_all() won't read until eof -- it just reads some data
- -- but it guarantees to read at least one byte unless EOF is hit.
- It is possible to pass a Telnet object to a selector in order to wait until
- more data is available. Note that in this case, read_eager() may return b''
- even if there was data on the socket, because the protocol negotiation may have
- eaten the data. This is why EOFError is needed in some cases to distinguish
- between "no data" and "connection closed" (since the socket also appears ready
- for reading when it is closed).
- To do:
- - option negotiation
- - timeout should be intrinsic to the connection object instead of an
- option on one of the read calls only
- """
- import sys
- import socket
- import selectors
- from time import monotonic as _time
- __all__ = ["Telnet"]
- DEBUGLEVEL = 0
- TELNET_PORT = 23
- IAC = bytes([255])
- DONT = bytes([254])
- DO = bytes([253])
- WONT = bytes([252])
- WILL = bytes([251])
- theNULL = bytes([0])
- SE = bytes([240])
- NOP = bytes([241])
- DM = bytes([242])
- BRK = bytes([243])
- IP = bytes([244])
- AO = bytes([245])
- AYT = bytes([246])
- EC = bytes([247])
- EL = bytes([248])
- GA = bytes([249])
- SB = bytes([250])
- BINARY = bytes([0])
- ECHO = bytes([1])
- RCP = bytes([2])
- SGA = bytes([3])
- NAMS = bytes([4])
- STATUS = bytes([5])
- TM = bytes([6])
- RCTE = bytes([7])
- NAOL = bytes([8])
- NAOP = bytes([9])
- NAOCRD = bytes([10])
- NAOHTS = bytes([11])
- NAOHTD = bytes([12])
- NAOFFD = bytes([13])
- NAOVTS = bytes([14])
- NAOVTD = bytes([15])
- NAOLFD = bytes([16])
- XASCII = bytes([17])
- LOGOUT = bytes([18])
- BM = bytes([19])
- DET = bytes([20])
- SUPDUP = bytes([21])
- SUPDUPOUTPUT = bytes([22])
- SNDLOC = bytes([23])
- TTYPE = bytes([24])
- EOR = bytes([25])
- TUID = bytes([26])
- OUTMRK = bytes([27])
- TTYLOC = bytes([28])
- VT3270REGIME = bytes([29])
- X3PAD = bytes([30])
- NAWS = bytes([31])
- TSPEED = bytes([32])
- LFLOW = bytes([33])
- LINEMODE = bytes([34])
- XDISPLOC = bytes([35])
- OLD_ENVIRON = bytes([36])
- AUTHENTICATION = bytes([37])
- ENCRYPT = bytes([38])
- NEW_ENVIRON = bytes([39])
- TN3270E = bytes([40])
- XAUTH = bytes([41])
- CHARSET = bytes([42])
- RSP = bytes([43])
- COM_PORT_OPTION = bytes([44])
- SUPPRESS_LOCAL_ECHO = bytes([45])
- TLS = bytes([46])
- KERMIT = bytes([47])
- SEND_URL = bytes([48])
- FORWARD_X = bytes([49])
- PRAGMA_LOGON = bytes([138])
- SSPI_LOGON = bytes([139])
- PRAGMA_HEARTBEAT = bytes([140])
- EXOPL = bytes([255])
- NOOPT = bytes([0])
- if hasattr(selectors, 'PollSelector'):
- _TelnetSelector = selectors.PollSelector
- else:
- _TelnetSelector = selectors.SelectSelector
- class Telnet:
- """Telnet interface class.
- An instance of this class represents a connection to a telnet
- server. The instance is initially not connected; the open()
- method must be used to establish a connection. Alternatively, the
- host name and optional port number can be passed to the
- constructor, too.
- Don't try to reopen an already connected instance.
- This class has many read_*() methods. Note that some of them
- raise EOFError when the end of the connection is read, because
- they can return an empty string for other reasons. See the
- individual doc strings.
- read_until(expected, [timeout])
- Read until the expected string has been seen, or a timeout is
- hit (default is no timeout); may block.
- read_all()
- Read all data until EOF; may block.
- read_some()
- Read at least one byte or EOF; may block.
- read_very_eager()
- Read all data available already queued or on the socket,
- without blocking.
- read_eager()
- Read either data already queued or some data available on the
- socket, without blocking.
- read_lazy()
- Read all data in the raw queue (processing it first), without
- doing any socket I/O.
- read_very_lazy()
- Reads all data in the cooked queue, without doing any socket
- I/O.
- read_sb_data()
- Reads available data between SB ... SE sequence. Don't block.
- set_option_negotiation_callback(callback)
- Each time a telnet option is read on the input flow, this callback
- (if set) is called with the following parameters :
- callback(telnet socket, command, option)
- option will be chr(0) when there is no option.
- No other action is done afterwards by telnetlib.
- """
- def __init__(self, host=None, port=0,
- timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
- """Constructor.
- When called without arguments, create an unconnected instance.
- With a hostname argument, it connects the instance; port number
- and timeout are optional.
- """
- self.debuglevel = DEBUGLEVEL
- self.host = host
- self.port = port
- self.timeout = timeout
- self.sock = None
- self.rawq = b''
- self.irawq = 0
- self.cookedq = b''
- self.eof = 0
- self.iacseq = b''
- self.sb = 0
- self.sbdataq = b''
- self.option_callback = None
- if host is not None:
- self.open(host, port, timeout)
- def open(self, host, port=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
- """Connect to a host.
- The optional second argument is the port number, which
- defaults to the standard telnet port (23).
- Don't try to reopen an already connected instance.
- """
- self.eof = 0
- if not port:
- port = TELNET_PORT
- self.host = host
- self.port = port
- self.timeout = timeout
- self.sock = socket.create_connection((host, port), timeout)
- def __del__(self):
- """Destructor -- close the connection."""
- self.close()
- def msg(self, msg, *args):
- """Print a debug message, when the debug level is > 0.
- If extra arguments are present, they are substituted in the
- message using the standard string formatting operator.
- """
- if self.debuglevel > 0:
- print('Telnet(%s,%s):' % (self.host, self.port), end=' ')
- if args:
- print(msg % args)
- else:
- print(msg)
- def set_debuglevel(self, debuglevel):
- """Set the debug level.
- The higher it is, the more debug output you get (on sys.stdout).
- """
- self.debuglevel = debuglevel
- def close(self):
- """Close the connection."""
- sock = self.sock
- self.sock = None
- self.eof = True
- self.iacseq = b''
- self.sb = 0
- if sock:
- sock.close()
- def get_socket(self):
- """Return the socket object used internally."""
- return self.sock
- def fileno(self):
- """Return the fileno() of the socket object used internally."""
- return self.sock.fileno()
- def write(self, buffer):
- """Write a string to the socket, doubling any IAC characters.
- Can block if the connection is blocked. May raise
- OSError if the connection is closed.
- """
- if IAC in buffer:
- buffer = buffer.replace(IAC, IAC+IAC)
- self.msg("send %r", buffer)
- self.sock.sendall(buffer)
- 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.
- """
- n = len(match)
- self.process_rawq()
- i = self.cookedq.find(match)
- if i >= 0:
- i = i+n
- buf = self.cookedq[:i]
- self.cookedq = self.cookedq[i:]
- return buf
- if timeout is not None:
- deadline = _time() + timeout
- with _TelnetSelector() as selector:
- selector.register(self, selectors.EVENT_READ)
- while not self.eof:
- if selector.select(timeout):
- i = max(0, len(self.cookedq)-n)
- self.fill_rawq()
- self.process_rawq()
- i = self.cookedq.find(match, i)
- if i >= 0:
- i = i+n
- buf = self.cookedq[:i]
- self.cookedq = self.cookedq[i:]
- return buf
- if timeout is not None:
- timeout = deadline - _time()
- if timeout < 0:
- break
- return self.read_very_lazy()
- def read_all(self):
- """Read all data until EOF; block until connection closed."""
- self.process_rawq()
- while not self.eof:
- self.fill_rawq()
- self.process_rawq()
- buf = self.cookedq
- self.cookedq = b''
- return buf
- def read_some(self):
- """Read at least one byte of cooked data unless EOF is hit.
- Return b'' if EOF is hit. Block if no data is immediately
- available.
- """
- self.process_rawq()
- while not self.cookedq and not self.eof:
- self.fill_rawq()
- self.process_rawq()
- buf = self.cookedq
- self.cookedq = b''
- return buf
- def read_very_eager(self):
- """Read everything that's possible without blocking in I/O (eager).
- Raise EOFError if connection closed and no cooked data
- available. Return b'' if no cooked data available otherwise.
- Don't block unless in the midst of an IAC sequence.
- """
- self.process_rawq()
- while not self.eof and self.sock_avail():
- self.fill_rawq()
- self.process_rawq()
- return self.read_very_lazy()
- def read_eager(self):
- """Read readily available data.
- Raise EOFError if connection closed and no cooked data
- available. Return b'' if no cooked data available otherwise.
- Don't block unless in the midst of an IAC sequence.
- """
- self.process_rawq()
- while not self.cookedq and not self.eof and self.sock_avail():
- self.fill_rawq()
- self.process_rawq()
- return self.read_very_lazy()
- def read_lazy(self):
- """Process and return data that's already in the queues (lazy).
- Raise EOFError if connection closed and no data available.
- Return b'' if no cooked data available otherwise. Don't block
- unless in the midst of an IAC sequence.
- """
- self.process_rawq()
- return self.read_very_lazy()
- def read_very_lazy(self):
- """Return any data available in the cooked queue (very lazy).
- Raise EOFError if connection closed and no data available.
- Return b'' if no cooked data available otherwise. Don't block.
- """
- buf = self.cookedq
- self.cookedq = b''
- if not buf and self.eof and not self.rawq:
- raise EOFError('telnet connection closed')
- return buf
- def read_sb_data(self):
- """Return any data available in the SB ... SE queue.
- Return b'' if no SB ... SE available. Should only be called
- after seeing a SB or SE command. When a new SB command is
- found, old unread SB data will be discarded. Don't block.
- """
- buf = self.sbdataq
- self.sbdataq = b''
- return buf
- def set_option_negotiation_callback(self, callback):
- """Provide a callback function called after each receipt of a telnet option."""
- self.option_callback = callback
- def process_rawq(self):
- """Transfer from raw queue to cooked queue.
- Set self.eof when connection is closed. Don't block unless in
- the midst of an IAC sequence.
- """
- buf = [b'', b'']
- try:
- while self.rawq:
- c = self.rawq_getchar()
- if not self.iacseq:
- if c == theNULL:
- continue
- if c == b"\021":
- continue
- if c != IAC:
- buf[self.sb] = buf[self.sb] + c
- continue
- else:
- self.iacseq += c
- elif len(self.iacseq) == 1:
-
- if c in (DO, DONT, WILL, WONT):
- self.iacseq += c
- continue
- self.iacseq = b''
- if c == IAC:
- buf[self.sb] = buf[self.sb] + c
- else:
- if c == SB:
- self.sb = 1
- self.sbdataq = b''
- elif c == SE:
- self.sb = 0
- self.sbdataq = self.sbdataq + buf[1]
- buf[1] = b''
- if self.option_callback:
-
-
- self.option_callback(self.sock, c, NOOPT)
- else:
-
-
-
- self.msg('IAC %d not recognized' % ord(c))
- elif len(self.iacseq) == 2:
- cmd = self.iacseq[1:2]
- self.iacseq = b''
- opt = c
- if cmd in (DO, DONT):
- self.msg('IAC %s %d',
- cmd == DO and 'DO' or 'DONT', ord(opt))
- if self.option_callback:
- self.option_callback(self.sock, cmd, opt)
- else:
- self.sock.sendall(IAC + WONT + opt)
- elif cmd in (WILL, WONT):
- self.msg('IAC %s %d',
- cmd == WILL and 'WILL' or 'WONT', ord(opt))
- if self.option_callback:
- self.option_callback(self.sock, cmd, opt)
- else:
- self.sock.sendall(IAC + DONT + opt)
- except EOFError:
- self.iacseq = b''
- self.sb = 0
- pass
- self.cookedq = self.cookedq + buf[0]
- self.sbdataq = self.sbdataq + buf[1]
- def rawq_getchar(self):
- """Get next char from raw queue.
- Block if no data is immediately available. Raise EOFError
- when connection is closed.
- """
- if not self.rawq:
- self.fill_rawq()
- if self.eof:
- raise EOFError
- c = self.rawq[self.irawq:self.irawq+1]
- self.irawq = self.irawq + 1
- if self.irawq >= len(self.rawq):
- self.rawq = b''
- self.irawq = 0
- return c
- def fill_rawq(self):
- """Fill raw queue from exactly one recv() system call.
- Block if no data is immediately available. Set self.eof when
- connection is closed.
- """
- if self.irawq >= len(self.rawq):
- self.rawq = b''
- self.irawq = 0
-
-
- buf = self.sock.recv(50)
- self.msg("recv %r", buf)
- self.eof = (not buf)
- self.rawq = self.rawq + buf
- def sock_avail(self):
- """Test whether data is available on the socket."""
- with _TelnetSelector() as selector:
- selector.register(self, selectors.EVENT_READ)
- return bool(selector.select(0))
- def interact(self):
- """Interaction function, emulates a very dumb telnet client."""
- if sys.platform == "win32":
- self.mt_interact()
- return
- with _TelnetSelector() as selector:
- selector.register(self, selectors.EVENT_READ)
- selector.register(sys.stdin, selectors.EVENT_READ)
- while True:
- for key, events in selector.select():
- if key.fileobj is self:
- try:
- text = self.read_eager()
- except EOFError:
- print('*** Connection closed by remote host ***')
- return
- if text:
- sys.stdout.write(text.decode('ascii'))
- sys.stdout.flush()
- elif key.fileobj is sys.stdin:
- line = sys.stdin.readline().encode('ascii')
- if not line:
- return
- self.write(line)
- def mt_interact(self):
- """Multithreaded version of interact()."""
- import _thread
- _thread.start_new_thread(self.listener, ())
- while 1:
- line = sys.stdin.readline()
- if not line:
- break
- self.write(line.encode('ascii'))
- def listener(self):
- """Helper for mt_interact() -- this executes in the other thread."""
- while 1:
- try:
- data = self.read_eager()
- except EOFError:
- print('*** Connection closed by remote host ***')
- return
- if data:
- sys.stdout.write(data.decode('ascii'))
- else:
- sys.stdout.flush()
- def expect(self, list, timeout=None):
- """Read until one from a list of a regular expressions matches.
- The first argument is a list of regular expressions, either
- compiled (re.RegexObject instances) or uncompiled (strings).
- The optional second argument is a timeout, in seconds; default
- is no timeout.
- Return a tuple of three items: the index in the list of the
- first regular expression that matches; the match object
- returned; and the text read up till and including the match.
- If EOF is read and no text was read, raise EOFError.
- Otherwise, when nothing matches, return (-1, None, text) where
- text is the text received so far (may be the empty string if a
- timeout happened).
- If a regular expression ends with a greedy match (e.g. '.*')
- or if more than one expression can match the same input, the
- results are undeterministic, and may depend on the I/O timing.
- """
- re = None
- list = list[:]
- indices = range(len(list))
- for i in indices:
- if not hasattr(list[i], "search"):
- if not re: import re
- list[i] = re.compile(list[i])
- if timeout is not None:
- deadline = _time() + timeout
- with _TelnetSelector() as selector:
- selector.register(self, selectors.EVENT_READ)
- while not self.eof:
- self.process_rawq()
- for i in indices:
- m = list[i].search(self.cookedq)
- if m:
- e = m.end()
- text = self.cookedq[:e]
- self.cookedq = self.cookedq[e:]
- return (i, m, text)
- if timeout is not None:
- ready = selector.select(timeout)
- timeout = deadline - _time()
- if not ready:
- if timeout < 0:
- break
- else:
- continue
- self.fill_rawq()
- text = self.read_very_lazy()
- if not text and self.eof:
- raise EOFError
- return (-1, None, text)
- def test():
- """Test program for telnetlib.
- Usage: python telnetlib.py [-d] ... [host [port]]
- Default host is localhost; default port is 23.
- """
- debuglevel = 0
- while sys.argv[1:] and sys.argv[1] == '-d':
- debuglevel = debuglevel+1
- del sys.argv[1]
- host = 'localhost'
- if sys.argv[1:]:
- host = sys.argv[1]
- port = 0
- if sys.argv[2:]:
- portstr = sys.argv[2]
- try:
- port = int(portstr)
- except ValueError:
- port = socket.getservbyname(portstr, 'tcp')
- tn = Telnet()
- tn.set_debuglevel(debuglevel)
- tn.open(host, port, timeout=0.5)
- tn.interact()
- tn.close()
- if __name__ == '__main__':
- test()
|