mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
Patch #630829: Don't block on IAC, process suboptions.
This commit is contained in:
parent
a178cff979
commit
1da9c57c74
4 changed files with 93 additions and 32 deletions
|
@ -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
|
||||||
|
|
113
Lib/telnetlib.py
113
Lib/telnetlib.py
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue