mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	Patch #630829: Don't block on IAC, process suboptions.
This commit is contained in:
		
							parent
							
								
									a178cff979
								
							
						
					
					
						commit
						1da9c57c74
					
				
					 4 changed files with 93 additions and 32 deletions
				
			
		| 
						 | 
					@ -96,6 +96,14 @@ Return \code{''} if no cooked data available otherwise.  This method
 | 
				
			||||||
never blocks.
 | 
					never blocks.
 | 
				
			||||||
\end{methoddesc}
 | 
					\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}}
 | 
					\begin{methoddesc}{open}{host\optional{, port}}
 | 
				
			||||||
Connect to a host.
 | 
					Connect to a host.
 | 
				
			||||||
The optional second argument is the port number, which
 | 
					The optional second argument is the port number, which
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,9 +25,6 @@
 | 
				
			||||||
"connection closed" (since the socket also appears ready for reading
 | 
					"connection closed" (since the socket also appears ready for reading
 | 
				
			||||||
when it is closed).
 | 
					when it is closed).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Bugs:
 | 
					 | 
				
			||||||
- may hang when connection is slow in the middle of an IAC sequence
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To do:
 | 
					To do:
 | 
				
			||||||
- option negotiation
 | 
					- option negotiation
 | 
				
			||||||
- timeout should be intrinsic to the connection object instead of an
 | 
					- timeout should be intrinsic to the connection object instead of an
 | 
				
			||||||
| 
						 | 
					@ -56,6 +53,8 @@
 | 
				
			||||||
WONT = chr(252)
 | 
					WONT = chr(252)
 | 
				
			||||||
WILL = chr(251)
 | 
					WILL = chr(251)
 | 
				
			||||||
theNULL = chr(0)
 | 
					theNULL = chr(0)
 | 
				
			||||||
 | 
					SB   = chr(250)
 | 
				
			||||||
 | 
					SE   = chr(240)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Telnet protocol options code (don't change)
 | 
					# Telnet protocol options code (don't change)
 | 
				
			||||||
# These ones all come from arpa/telnet.h
 | 
					# These ones all come from arpa/telnet.h
 | 
				
			||||||
| 
						 | 
					@ -117,6 +116,7 @@
 | 
				
			||||||
SSPI_LOGON = chr(139) # TELOPT SSPI LOGON
 | 
					SSPI_LOGON = chr(139) # TELOPT SSPI LOGON
 | 
				
			||||||
PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT
 | 
					PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT
 | 
				
			||||||
EXOPL = chr(255) # Extended-Options-List
 | 
					EXOPL = chr(255) # Extended-Options-List
 | 
				
			||||||
 | 
					NOOPT = chr(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Telnet:
 | 
					class Telnet:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,10 +161,14 @@ class Telnet:
 | 
				
			||||||
        Reads all data in the cooked queue, without doing any socket
 | 
					        Reads all data in the cooked queue, without doing any socket
 | 
				
			||||||
        I/O.
 | 
					        I/O.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    read_sb_data()
 | 
				
			||||||
 | 
					        Reads available data between SB ... SE sequence. Don't block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    set_option_negotiation_callback(callback)
 | 
					    set_option_negotiation_callback(callback)
 | 
				
			||||||
        Each time a telnet option is read on the input flow, this callback
 | 
					        Each time a telnet option is read on the input flow, this callback
 | 
				
			||||||
        (if set) is called with the following parameters :
 | 
					        (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.
 | 
					        No other action is done afterwards by telnetlib.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -185,6 +189,9 @@ def __init__(self, host=None, port=0):
 | 
				
			||||||
        self.irawq = 0
 | 
					        self.irawq = 0
 | 
				
			||||||
        self.cookedq = ''
 | 
					        self.cookedq = ''
 | 
				
			||||||
        self.eof = 0
 | 
					        self.eof = 0
 | 
				
			||||||
 | 
					        self.iacseq = '' # Buffer for IAC sequence.
 | 
				
			||||||
 | 
					        self.sb = 0 # flag for SB and SE sequence.
 | 
				
			||||||
 | 
					        self.sbdataq = ''
 | 
				
			||||||
        self.option_callback = None
 | 
					        self.option_callback = None
 | 
				
			||||||
        if host is not None:
 | 
					        if host is not None:
 | 
				
			||||||
            self.open(host, port)
 | 
					            self.open(host, port)
 | 
				
			||||||
| 
						 | 
					@ -250,6 +257,8 @@ def close(self):
 | 
				
			||||||
            self.sock.close()
 | 
					            self.sock.close()
 | 
				
			||||||
        self.sock = 0
 | 
					        self.sock = 0
 | 
				
			||||||
        self.eof = 1
 | 
					        self.eof = 1
 | 
				
			||||||
 | 
					        self.iacseq = ''
 | 
				
			||||||
 | 
					        self.sb = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_socket(self):
 | 
					    def get_socket(self):
 | 
				
			||||||
        """Return the socket object used internally."""
 | 
					        """Return the socket object used internally."""
 | 
				
			||||||
| 
						 | 
					@ -380,6 +389,18 @@ def read_very_lazy(self):
 | 
				
			||||||
            raise EOFError, 'telnet connection closed'
 | 
					            raise EOFError, 'telnet connection closed'
 | 
				
			||||||
        return buf
 | 
					        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):
 | 
					    def set_option_negotiation_callback(self, callback):
 | 
				
			||||||
        """Provide a callback function called after each receipt of a telnet option."""
 | 
					        """Provide a callback function called after each receipt of a telnet option."""
 | 
				
			||||||
        self.option_callback = callback
 | 
					        self.option_callback = callback
 | 
				
			||||||
| 
						 | 
					@ -391,40 +412,70 @@ def process_rawq(self):
 | 
				
			||||||
        the midst of an IAC sequence.
 | 
					        the midst of an IAC sequence.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        buf = ''
 | 
					        buf = ['', '']
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            while self.rawq:
 | 
					            while self.rawq:
 | 
				
			||||||
                c = self.rawq_getchar()
 | 
					                c = self.rawq_getchar()
 | 
				
			||||||
 | 
					                if not self.iacseq:
 | 
				
			||||||
                    if c == theNULL:
 | 
					                    if c == theNULL:
 | 
				
			||||||
                        continue
 | 
					                        continue
 | 
				
			||||||
                    if c == "\021":
 | 
					                    if c == "\021":
 | 
				
			||||||
                        continue
 | 
					                        continue
 | 
				
			||||||
                    if c != IAC:
 | 
					                    if c != IAC:
 | 
				
			||||||
                    buf = buf + c
 | 
					                        buf[self.sb] = buf[self.sb] + c
 | 
				
			||||||
                        continue
 | 
					                        continue
 | 
				
			||||||
                c = self.rawq_getchar()
 | 
					                    else:
 | 
				
			||||||
 | 
					                        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:
 | 
					                    if c == IAC:
 | 
				
			||||||
                    buf = buf + c
 | 
					                        buf[self.sb] = buf[self.sb] + c
 | 
				
			||||||
                elif c in (DO, DONT):
 | 
					                    else:
 | 
				
			||||||
                    opt = self.rawq_getchar()
 | 
					                        if c == SB: # SB ... SE start.
 | 
				
			||||||
                    self.msg('IAC %s %d', c == DO and 'DO' or 'DONT', ord(opt))
 | 
					                            self.sb = 1
 | 
				
			||||||
 | 
					                            self.sbdataq = ''
 | 
				
			||||||
 | 
					                        elif c == SE:
 | 
				
			||||||
 | 
					                            self.sb = 0
 | 
				
			||||||
 | 
					                            self.sbdataq = self.sbdataq + buf[1]
 | 
				
			||||||
 | 
					                            buf[1] = ''
 | 
				
			||||||
                        if self.option_callback:
 | 
					                        if self.option_callback:
 | 
				
			||||||
                        self.option_callback(self.sock, c, opt)
 | 
					                            # 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:
 | 
					                        else:
 | 
				
			||||||
                            self.sock.sendall(IAC + WONT + opt)
 | 
					                            self.sock.sendall(IAC + WONT + opt)
 | 
				
			||||||
                elif c in (WILL, WONT):
 | 
					                    elif cmd in (WILL, WONT):
 | 
				
			||||||
                    opt = self.rawq_getchar()
 | 
					 | 
				
			||||||
                        self.msg('IAC %s %d',
 | 
					                        self.msg('IAC %s %d',
 | 
				
			||||||
                             c == WILL and 'WILL' or 'WONT', ord(opt))
 | 
					                            cmd == WILL and 'WILL' or 'WONT', ord(opt))
 | 
				
			||||||
                        if self.option_callback:
 | 
					                        if self.option_callback:
 | 
				
			||||||
                        self.option_callback(self.sock, c, opt)
 | 
					                            self.option_callback(self.sock, cmd, opt)
 | 
				
			||||||
                        else:
 | 
					                        else:
 | 
				
			||||||
                            self.sock.sendall(IAC + DONT + opt)
 | 
					                            self.sock.sendall(IAC + DONT + opt)
 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    self.msg('IAC %d not recognized' % ord(c))
 | 
					 | 
				
			||||||
        except EOFError: # raised by self.rawq_getchar()
 | 
					        except EOFError: # raised by self.rawq_getchar()
 | 
				
			||||||
 | 
					            self.iacseq = '' # Reset on EOF
 | 
				
			||||||
 | 
					            self.sb = 0
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
        self.cookedq = self.cookedq + buf
 | 
					        self.cookedq = self.cookedq + buf[0]
 | 
				
			||||||
 | 
					        self.sbdataq = self.sbdataq + buf[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def rawq_getchar(self):
 | 
					    def rawq_getchar(self):
 | 
				
			||||||
        """Get next char from raw queue.
 | 
					        """Get next char from raw queue.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -465,6 +465,7 @@ Steven Scott
 | 
				
			||||||
Nick Seidenman
 | 
					Nick Seidenman
 | 
				
			||||||
Fred Sells
 | 
					Fred Sells
 | 
				
			||||||
Denis Severson
 | 
					Denis Severson
 | 
				
			||||||
 | 
					Ha Shao
 | 
				
			||||||
Bruce Sherwood
 | 
					Bruce Sherwood
 | 
				
			||||||
Pete Shinners
 | 
					Pete Shinners
 | 
				
			||||||
Michael Shiplett
 | 
					Michael Shiplett
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1707,7 +1707,8 @@ Library
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- telnetlib includes symbolic names for the options, and support for
 | 
					- 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
 | 
					- The new C standard no longer requires that math libraries set errno to
 | 
				
			||||||
  ERANGE on overflow.  For platform libraries that exploit this new
 | 
					  ERANGE on overflow.  For platform libraries that exploit this new
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue