--- telnetlib.py-orig 2006-05-08 12:56:13.000000000 +-0200 +++ telnetlib.py 2006-07-10 09:21:30.000000000 +-0200 @@ -22,14 +22,16 @@ read_eager() may return '' even if there was data on the socket, because the protocol negotiation may have eaten the data. This is why 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). +Option negotiation is not done within telnetlib as options are +situation sensitive and should be done at the application level. + To do: -- option negotiation - timeout should be intrinsic to the connection object instead of an option on one of the read calls only """ @@ -44,19 +46,20 @@ DEBUGLEVEL = 0 # Telnet protocol defaults TELNET_PORT = 23 # Telnet protocol characters (don't change) -IAC = chr(255) # "Interpret As Command" -DONT = chr(254) -DO = chr(253) -WONT = chr(252) -WILL = chr(251) +IAC = chr(255) # 0xFF "Interpret As Command" +DONT = chr(254) # 0xFE "Please dont do ..." +DO = chr(253) # 0xFD "Please do ..." +WONT = chr(252) # 0xFC "No, We will not do ..." +WILL = chr(251) # 0xFB "Yes, We will do ..." theNULL = chr(0) +EORi = chr(239) # End of Record indication SE = chr(240) # Subnegotiation End NOP = chr(241) # No Operation DM = chr(242) # Data Mark BRK = chr(243) # Break IP = chr(244) # Interrupt process AO = chr(245) # Abort output @@ -169,21 +172,80 @@ doing any socket I/O. read_very_lazy() Reads all data in the cooked queue, without doing any socket I/O. + + Option Handling features: + + A mechanisme specified in the telnet RFC is used for the exchange + of settings, or options, between the telnet client and server. + These options have the form of DOs and DONTs, to which the other + side replies with WILLs and WONTs. + + The default telnetlib action when receiving any WILL or DO option + is to reply a WONT or DONT, and reception of any subsection request + are simply ignored. + + Handling of these options is situation sentitive and can beter be + done by the calling application. + + To facilitate this, telnetlib provide two ways to add the needed + handling functionality. The first is to derive a new class from + Telnet, and to override method handle_option() + + The second way, to be used if deriving a new class is not wanted, + a callback function can be registered using + set_option_negotiation_callback() + This function will then be called for each received option. + + Furthermore, to handle Subsection options, which are variable + length requests limited by a SB and SE sequence, the method + read_sb_data() is provided. A good place to use this method + would be from within the option handling functions. + + Final notes: + - For backwards compatibily, the callback mechanisme + will overrule the handle_option method. + - Neither the overidden handle_option() nor the callback + function should block. + read_sb_data() - Reads available data between SB ... SE sequence. Don't block. + Reads available subsection data found between between SB...SE + option sequences. This function will never block. + If this data is to be handled, than the user should be aware of + the following: + - Any subsection data currently present is deleted at reception + of a SB (Subsection Begin); + - This function will only return anything meaningfull just after + reception of the SE (Subsection End); + - The current subsection data is deleted after calling read_sb_data(). + def handle_option(self, command, option) + This method is called internally to handle telnet options. + + Within telnetlib, this method does not handle anything. It is up + to the user to derive the Telnet class, and to overridde this method. + + If the overridden method successfully handles the option it should + return True, else the default action is taken (which is usually to + reply WONT or DONT with the requested option) + + For backwards compatibility, if set_option_negotiation_callback() + has been called to set up a callback(), then handle_option() is not + called anymore. + 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, option) option will be chr(0) when there is no option. - No other action is done afterwards by telnetlib. + + If this callback is not set, method handle_option() is called (see + above). """ def __init__(self, host=None, port=0): """Constructor. @@ -410,14 +472,29 @@ """ 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.""" + """ Provide a callback function called after each receipt of a + telnet option. + If used, this mechanisme overrules the handle_option() method, + which will not be called anymore. + """ self.option_callback = callback + + def handle_option(self, cmd, opt): + """ This function is called internally to handle telnet options. + + To do anything, the Telnet class should be extended, and + this call should be overridden. + If the overridden function succeeds it should return True + else the default action is taken (which is usually WONT or DONT) + + """ + return False # to do anything this call must be overridden def process_rawq(self): """Transfer from raw queue to cooked queue. Set self.eof when connection is closed. Don't block unless in the midst of an IAC sequence. @@ -452,37 +529,40 @@ 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 + # if provided, Callback is supposed to look into # the sbdataq self.option_callback(self.sock, c, NOOPT) - else: + + # handle_option(), if overridden (the current one doesn't do much) + # is supposed to look into the sbdata + elif not self.handle_option(c, NOOPT): # 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)) + self.msg('IAC %d not handled' % 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: + elif not self.handle_option (cmd, opt): 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: + elif not self.handle_option (cmd, opt): 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[0]