]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Patch #630829: Don't block on IAC, process suboptions.
authorMartin v. Löwis <martin@v.loewis.de>
Mon, 4 Nov 2002 09:56:00 +0000 (09:56 +0000)
committerMartin v. Löwis <martin@v.loewis.de>
Mon, 4 Nov 2002 09:56:00 +0000 (09:56 +0000)
Doc/lib/libtelnetlib.tex
Lib/telnetlib.py
Misc/ACKS
Misc/NEWS

index fe56f72126c05a159a89f7576d1ea4c8e3e22bbf..83f6b43e4dc95014ce984126b0739238fff65a02 100644 (file)
@@ -96,6 +96,14 @@ Return \code{''} if no cooked data available otherwise.  This method
 never blocks.
 \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}}
 Connect to a host.
 The optional second argument is the port number, which
index 4c806db22e03d15eac962aa8860ad3c4cca4a590..fe0672a9a1a4e58d54f6ff47d22fffb19a535e78 100644 (file)
@@ -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.
index 901bb45970bd947595ebbced5b81cf08425acff1..4b496f25161e6cb5409f2f95791d8197fdcaf9e5 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -465,6 +465,7 @@ Steven Scott
 Nick Seidenman
 Fred Sells
 Denis Severson
+Ha Shao
 Bruce Sherwood
 Pete Shinners
 Michael Shiplett
index 71eacc84d4cc6a7cb58d38c18bf203862834e364..c8334d70c4e69dbf179fd2122db4df279f8cc6b3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1707,7 +1707,8 @@ Library
 -------
 
 - 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
   ERANGE on overflow.  For platform libraries that exploit this new