Patch #630829: Don't block on IAC, process suboptions.

This commit is contained in:
Martin v. Löwis 2002-11-04 09:56:00 +00:00
parent a178cff979
commit 1da9c57c74
4 changed files with 93 additions and 32 deletions

View file

@ -96,6 +96,14 @@ Return \code{''} if no cooked data available otherwise. This method
never blocks. never blocks.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}{read_sb_data}{}
Return the data collected between a SB/SE pair (suboption begin/end).
The callback should access these data when it was invoked with a
\code{SE} command. This method never blocks.
\versionadded{2.3}
\end{methoddesc}
\begin{methoddesc}{open}{host\optional{, port}} \begin{methoddesc}{open}{host\optional{, port}}
Connect to a host. Connect to a host.
The optional second argument is the port number, which The optional second argument is the port number, which

View file

@ -25,9 +25,6 @@ EOFError is needed in some cases to distinguish between "no data" and
"connection closed" (since the socket also appears ready for reading "connection closed" (since the socket also appears ready for reading
when it is closed). when it is closed).
Bugs:
- may hang when connection is slow in the middle of an IAC sequence
To do: To do:
- option negotiation - option negotiation
- timeout should be intrinsic to the connection object instead of an - timeout should be intrinsic to the connection object instead of an
@ -56,6 +53,8 @@ DO = chr(253)
WONT = chr(252) WONT = chr(252)
WILL = chr(251) WILL = chr(251)
theNULL = chr(0) theNULL = chr(0)
SB = chr(250)
SE = chr(240)
# Telnet protocol options code (don't change) # Telnet protocol options code (don't change)
# These ones all come from arpa/telnet.h # These ones all come from arpa/telnet.h
@ -117,6 +116,7 @@ PRAGMA_LOGON = chr(138) # TELOPT PRAGMA LOGON
SSPI_LOGON = chr(139) # TELOPT SSPI LOGON SSPI_LOGON = chr(139) # TELOPT SSPI LOGON
PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT
EXOPL = chr(255) # Extended-Options-List EXOPL = chr(255) # Extended-Options-List
NOOPT = chr(0)
class Telnet: class Telnet:
@ -161,10 +161,14 @@ class Telnet:
Reads all data in the cooked queue, without doing any socket Reads all data in the cooked queue, without doing any socket
I/O. I/O.
read_sb_data()
Reads available data between SB ... SE sequence. Don't block.
set_option_negotiation_callback(callback) set_option_negotiation_callback(callback)
Each time a telnet option is read on the input flow, this callback Each time a telnet option is read on the input flow, this callback
(if set) is called with the following parameters : (if set) is called with the following parameters :
callback(telnet socket, command (DO/DONT/WILL/WONT), option) callback(telnet socket, command, option)
option will be chr(0) when there is no option.
No other action is done afterwards by telnetlib. No other action is done afterwards by telnetlib.
""" """
@ -185,6 +189,9 @@ class Telnet:
self.irawq = 0 self.irawq = 0
self.cookedq = '' self.cookedq = ''
self.eof = 0 self.eof = 0
self.iacseq = '' # Buffer for IAC sequence.
self.sb = 0 # flag for SB and SE sequence.
self.sbdataq = ''
self.option_callback = None self.option_callback = None
if host is not None: if host is not None:
self.open(host, port) self.open(host, port)
@ -250,6 +257,8 @@ class Telnet:
self.sock.close() self.sock.close()
self.sock = 0 self.sock = 0
self.eof = 1 self.eof = 1
self.iacseq = ''
self.sb = 0
def get_socket(self): def get_socket(self):
"""Return the socket object used internally.""" """Return the socket object used internally."""
@ -379,6 +388,18 @@ class Telnet:
if not buf and self.eof and not self.rawq: if not buf and self.eof and not self.rawq:
raise EOFError, 'telnet connection closed' raise EOFError, 'telnet connection closed'
return buf return buf
def read_sb_data(self):
"""Return any data available in the SB ... SE queue.
Return '' 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 = ''
return buf
def set_option_negotiation_callback(self, callback): def set_option_negotiation_callback(self, callback):
"""Provide a callback function called after each receipt of a telnet option.""" """Provide a callback function called after each receipt of a telnet option."""
@ -391,40 +412,70 @@ class Telnet:
the midst of an IAC sequence. the midst of an IAC sequence.
""" """
buf = '' buf = ['', '']
try: try:
while self.rawq: while self.rawq:
c = self.rawq_getchar() c = self.rawq_getchar()
if c == theNULL: if not self.iacseq:
continue if c == theNULL:
if c == "\021": continue
continue if c == "\021":
if c != IAC: continue
buf = buf + c if c != IAC:
continue buf[self.sb] = buf[self.sb] + c
c = self.rawq_getchar() continue
if c == IAC:
buf = buf + c
elif c in (DO, DONT):
opt = self.rawq_getchar()
self.msg('IAC %s %d', c == DO and 'DO' or 'DONT', ord(opt))
if self.option_callback:
self.option_callback(self.sock, c, opt)
else: else:
self.sock.sendall(IAC + WONT + opt) self.iacseq += c
elif c in (WILL, WONT): elif len(self.iacseq) == 1:
opt = self.rawq_getchar() 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]'
self.msg('IAC %s %d', if c in (DO, DONT, WILL, WONT):
c == WILL and 'WILL' or 'WONT', ord(opt)) self.iacseq += c
if self.option_callback: continue
self.option_callback(self.sock, c, opt)
self.iacseq = ''
if c == IAC:
buf[self.sb] = buf[self.sb] + c
else: else:
self.sock.sendall(IAC + DONT + opt) if c == SB: # SB ... SE start.
else: self.sb = 1
self.msg('IAC %d not recognized' % ord(c)) self.sbdataq = ''
elif c == SE:
self.sb = 0
self.sbdataq = self.sbdataq + buf[1]
buf[1] = ''
if self.option_callback:
# Callback is supposed to look into
# the sbdataq
self.option_callback(self.sock, c, NOOPT)
else:
# We can't offer automatic processing of
# suboptions. Alas, we should not get any
# unless we did a WILL/DO before.
self.msg('IAC %d not recognized' % ord(c))
elif len(self.iacseq) == 2:
cmd = self.iacseq[1]
self.iacseq = ''
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: # raised by self.rawq_getchar() except EOFError: # raised by self.rawq_getchar()
self.iacseq = '' # Reset on EOF
self.sb = 0
pass pass
self.cookedq = self.cookedq + buf self.cookedq = self.cookedq + buf[0]
self.sbdataq = self.sbdataq + buf[1]
def rawq_getchar(self): def rawq_getchar(self):
"""Get next char from raw queue. """Get next char from raw queue.

View file

@ -465,6 +465,7 @@ Steven Scott
Nick Seidenman Nick Seidenman
Fred Sells Fred Sells
Denis Severson Denis Severson
Ha Shao
Bruce Sherwood Bruce Sherwood
Pete Shinners Pete Shinners
Michael Shiplett Michael Shiplett

View file

@ -1707,7 +1707,8 @@ Library
------- -------
- telnetlib includes symbolic names for the options, and support for - telnetlib includes symbolic names for the options, and support for
setting an option negotiation callback. setting an option negotiation callback. It also supports processing
of suboptions.
- The new C standard no longer requires that math libraries set errno to - The new C standard no longer requires that math libraries set errno to
ERANGE on overflow. For platform libraries that exploit this new ERANGE on overflow. For platform libraries that exploit this new