2525"connection closed" (since the socket also appears ready for reading
2626when it is closed).
2727
28- Bugs:
29- - may hang when connection is slow in the middle of an IAC sequence
30-
3128To do:
3229- option negotiation
3330- timeout should be intrinsic to the connection object instead of an
5653WONT = chr (252 )
5754WILL = chr (251 )
5855theNULL = 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
117116SSPI_LOGON = chr (139 ) # TELOPT SSPI LOGON
118117PRAGMA_HEARTBEAT = chr (140 ) # TELOPT PRAGMA HEARTBEAT
119118EXOPL = chr (255 ) # Extended-Options-List
119+ NOOPT = chr (0 )
120120
121121class 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.
0 commit comments