diff options
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.  | 
