Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1da9c57

Browse files
committed
Patch #630829: Don't block on IAC, process suboptions.
1 parent a178cff commit 1da9c57

4 files changed

Lines changed: 93 additions & 32 deletions

File tree

Doc/lib/libtelnetlib.tex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ \subsection{Telnet Objects \label{telnet-objects}}
9696
never blocks.
9797
\end{methoddesc}
9898

99+
\begin{methoddesc}{read_sb_data}{}
100+
Return the data collected between a SB/SE pair (suboption begin/end).
101+
The callback should access these data when it was invoked with a
102+
\code{SE} command. This method never blocks.
103+
104+
\versionadded{2.3}
105+
\end{methoddesc}
106+
99107
\begin{methoddesc}{open}{host\optional{, port}}
100108
Connect to a host.
101109
The optional second argument is the port number, which

Lib/telnetlib.py

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
"connection closed" (since the socket also appears ready for reading
2626
when it is closed).
2727
28-
Bugs:
29-
- may hang when connection is slow in the middle of an IAC sequence
30-
3128
To do:
3229
- option negotiation
3330
- timeout should be intrinsic to the connection object instead of an
@@ -56,6 +53,8 @@
5653
WONT = chr(252)
5754
WILL = chr(251)
5855
theNULL = chr(0)
56+
SB = chr(250)
57+
SE = chr(240)
5958

6059
# Telnet protocol options code (don't change)
6160
# These ones all come from arpa/telnet.h
@@ -117,6 +116,7 @@
117116
SSPI_LOGON = chr(139) # TELOPT SSPI LOGON
118117
PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT
119118
EXOPL = chr(255) # Extended-Options-List
119+
NOOPT = chr(0)
120120

121121
class Telnet:
122122

@@ -161,10 +161,14 @@ class Telnet:
161161
Reads all data in the cooked queue, without doing any socket
162162
I/O.
163163
164+
read_sb_data()
165+
Reads available data between SB ... SE sequence. Don't block.
166+
164167
set_option_negotiation_callback(callback)
165168
Each time a telnet option is read on the input flow, this callback
166169
(if set) is called with the following parameters :
167-
callback(telnet socket, command (DO/DONT/WILL/WONT), option)
170+
callback(telnet socket, command, option)
171+
option will be chr(0) when there is no option.
168172
No other action is done afterwards by telnetlib.
169173
170174
"""
@@ -185,6 +189,9 @@ def __init__(self, host=None, port=0):
185189
self.irawq = 0
186190
self.cookedq = ''
187191
self.eof = 0
192+
self.iacseq = '' # Buffer for IAC sequence.
193+
self.sb = 0 # flag for SB and SE sequence.
194+
self.sbdataq = ''
188195
self.option_callback = None
189196
if host is not None:
190197
self.open(host, port)
@@ -250,6 +257,8 @@ def close(self):
250257
self.sock.close()
251258
self.sock = 0
252259
self.eof = 1
260+
self.iacseq = ''
261+
self.sb = 0
253262

254263
def get_socket(self):
255264
"""Return the socket object used internally."""
@@ -379,6 +388,18 @@ def read_very_lazy(self):
379388
if not buf and self.eof and not self.rawq:
380389
raise EOFError, 'telnet connection closed'
381390
return buf
391+
392+
def read_sb_data(self):
393+
"""Return any data available in the SB ... SE queue.
394+
395+
Return '' if no SB ... SE available. Should only be called
396+
after seeing a SB or SE command. When a new SB command is
397+
found, old unread SB data will be discarded. Don't block.
398+
399+
"""
400+
buf = self.sbdataq
401+
self.sbdataq = ''
402+
return buf
382403

383404
def set_option_negotiation_callback(self, callback):
384405
"""Provide a callback function called after each receipt of a telnet option."""
@@ -391,40 +412,70 @@ def process_rawq(self):
391412
the midst of an IAC sequence.
392413
393414
"""
394-
buf = ''
415+
buf = ['', '']
395416
try:
396417
while self.rawq:
397418
c = self.rawq_getchar()
398-
if c == theNULL:
399-
continue
400-
if c == "\021":
401-
continue
402-
if c != IAC:
403-
buf = buf + c
404-
continue
405-
c = self.rawq_getchar()
406-
if c == IAC:
407-
buf = buf + c
408-
elif c in (DO, DONT):
409-
opt = self.rawq_getchar()
410-
self.msg('IAC %s %d', c == DO and 'DO' or 'DONT', ord(opt))
411-
if self.option_callback:
412-
self.option_callback(self.sock, c, opt)
419+
if not self.iacseq:
420+
if c == theNULL:
421+
continue
422+
if c == "\021":
423+
continue
424+
if c != IAC:
425+
buf[self.sb] = buf[self.sb] + c
426+
continue
413427
else:
414-
self.sock.sendall(IAC + WONT + opt)
415-
elif c in (WILL, WONT):
416-
opt = self.rawq_getchar()
417-
self.msg('IAC %s %d',
418-
c == WILL and 'WILL' or 'WONT', ord(opt))
419-
if self.option_callback:
420-
self.option_callback(self.sock, c, opt)
428+
self.iacseq += c
429+
elif len(self.iacseq) == 1:
430+
'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]'
431+
if c in (DO, DONT, WILL, WONT):
432+
self.iacseq += c
433+
continue
434+
435+
self.iacseq = ''
436+
if c == IAC:
437+
buf[self.sb] = buf[self.sb] + c
421438
else:
422-
self.sock.sendall(IAC + DONT + opt)
423-
else:
424-
self.msg('IAC %d not recognized' % ord(c))
439+
if c == SB: # SB ... SE start.
440+
self.sb = 1
441+
self.sbdataq = ''
442+
elif c == SE:
443+
self.sb = 0
444+
self.sbdataq = self.sbdataq + buf[1]
445+
buf[1] = ''
446+
if self.option_callback:
447+
# Callback is supposed to look into
448+
# the sbdataq
449+
self.option_callback(self.sock, c, NOOPT)
450+
else:
451+
# We can't offer automatic processing of
452+
# suboptions. Alas, we should not get any
453+
# unless we did a WILL/DO before.
454+
self.msg('IAC %d not recognized' % ord(c))
455+
elif len(self.iacseq) == 2:
456+
cmd = self.iacseq[1]
457+
self.iacseq = ''
458+
opt = c
459+
if cmd in (DO, DONT):
460+
self.msg('IAC %s %d',
461+
cmd == DO and 'DO' or 'DONT', ord(opt))
462+
if self.option_callback:
463+
self.option_callback(self.sock, cmd, opt)
464+
else:
465+
self.sock.sendall(IAC + WONT + opt)
466+
elif cmd in (WILL, WONT):
467+
self.msg('IAC %s %d',
468+
cmd == WILL and 'WILL' or 'WONT', ord(opt))
469+
if self.option_callback:
470+
self.option_callback(self.sock, cmd, opt)
471+
else:
472+
self.sock.sendall(IAC + DONT + opt)
425473
except EOFError: # raised by self.rawq_getchar()
474+
self.iacseq = '' # Reset on EOF
475+
self.sb = 0
426476
pass
427-
self.cookedq = self.cookedq + buf
477+
self.cookedq = self.cookedq + buf[0]
478+
self.sbdataq = self.sbdataq + buf[1]
428479

429480
def rawq_getchar(self):
430481
"""Get next char from raw queue.

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ Steven Scott
465465
Nick Seidenman
466466
Fred Sells
467467
Denis Severson
468+
Ha Shao
468469
Bruce Sherwood
469470
Pete Shinners
470471
Michael Shiplett

Misc/NEWS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1707,7 +1707,8 @@ Library
17071707
-------
17081708

17091709
- telnetlib includes symbolic names for the options, and support for
1710-
setting an option negotiation callback.
1710+
setting an option negotiation callback. It also supports processing
1711+
of suboptions.
17111712

17121713
- The new C standard no longer requires that math libraries set errno to
17131714
ERANGE on overflow. For platform libraries that exploit this new

0 commit comments

Comments
 (0)