diff options
author | Martin v. Löwis <martin@v.loewis.de> | 2002-11-04 09:56:00 (GMT) |
---|---|---|
committer | Martin v. Löwis <martin@v.loewis.de> | 2002-11-04 09:56:00 (GMT) |
commit | 1da9c57c740544fa0d2bc93949aa577b6184c8d2 (patch) | |
tree | 9e918767d7aae036025ba9636bfeb54b8f2d3add /Lib/telnetlib.py | |
parent | a178cff979b37771b3da710c23d579cd4abf0f00 (diff) | |
download | cpython-1da9c57c740544fa0d2bc93949aa577b6184c8d2.zip cpython-1da9c57c740544fa0d2bc93949aa577b6184c8d2.tar.gz cpython-1da9c57c740544fa0d2bc93949aa577b6184c8d2.tar.bz2 |
Patch #630829: Don't block on IAC, process suboptions.
Diffstat (limited to 'Lib/telnetlib.py')
-rw-r--r-- | Lib/telnetlib.py | 113 |
1 files changed, 82 insertions, 31 deletions
diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index 4c806db..fe0672a 100644 --- a/Lib/telnetlib.py +++ b/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 when it is closed). -Bugs: -- may hang when connection is slow in the middle of an IAC sequence - To do: - option negotiation - timeout should be intrinsic to the connection object instead of an @@ -56,6 +53,8 @@ DO = chr(253) WONT = chr(252) WILL = chr(251) theNULL = chr(0) +SB = chr(250) +SE = chr(240) # Telnet protocol options code (don't change) # 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 PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT EXOPL = chr(255) # Extended-Options-List +NOOPT = chr(0) class Telnet: @@ -161,10 +161,14 @@ class Telnet: 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 (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. """ @@ -185,6 +189,9 @@ class Telnet: self.irawq = 0 self.cookedq = '' self.eof = 0 + self.iacseq = '' # Buffer for IAC sequence. + self.sb = 0 # flag for SB and SE sequence. + self.sbdataq = '' self.option_callback = None if host is not None: self.open(host, port) @@ -250,6 +257,8 @@ class Telnet: self.sock.close() self.sock = 0 self.eof = 1 + self.iacseq = '' + self.sb = 0 def get_socket(self): """Return the socket object used internally.""" @@ -379,6 +388,18 @@ class Telnet: 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 '' 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): """Provide a callback function called after each receipt of a telnet option.""" @@ -391,40 +412,70 @@ class Telnet: the midst of an IAC sequence. """ - buf = '' + buf = ['', ''] try: while self.rawq: c = self.rawq_getchar() - if c == theNULL: - continue - if c == "\021": - continue - if c != IAC: - buf = buf + c - continue - c = self.rawq_getchar() - 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) + if not self.iacseq: + if c == theNULL: + continue + if c == "\021": + continue + if c != IAC: + buf[self.sb] = buf[self.sb] + c + continue else: - self.sock.sendall(IAC + WONT + opt) - elif c in (WILL, WONT): - opt = self.rawq_getchar() - self.msg('IAC %s %d', - c == WILL and 'WILL' or 'WONT', ord(opt)) - if self.option_callback: - self.option_callback(self.sock, c, opt) + self.iacseq += c + elif len(self.iacseq) == 1: + 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]' + if c in (DO, DONT, WILL, WONT): + self.iacseq += c + continue + + self.iacseq = '' + if c == IAC: + buf[self.sb] = buf[self.sb] + c else: - self.sock.sendall(IAC + DONT + opt) - else: - self.msg('IAC %d not recognized' % ord(c)) + if c == SB: # SB ... SE start. + self.sb = 1 + 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() + self.iacseq = '' # Reset on EOF + self.sb = 0 pass - self.cookedq = self.cookedq + buf + self.cookedq = self.cookedq + buf[0] + self.sbdataq = self.sbdataq + buf[1] def rawq_getchar(self): """Get next char from raw queue. |