| OLD | NEW |
| (Empty) |
| 1 | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 | |
| 6 """TELNET implementation, with line-oriented command handling. | |
| 7 """ | |
| 8 | |
| 9 import warnings | |
| 10 warnings.warn( | |
| 11 "As of Twisted 2.1, twisted.protocols.telnet is deprecated. " | |
| 12 "See twisted.conch.telnet for the current, supported API.", | |
| 13 DeprecationWarning, | |
| 14 stacklevel=2) | |
| 15 | |
| 16 | |
| 17 # System Imports | |
| 18 try: | |
| 19 from cStringIO import StringIO | |
| 20 except ImportError: | |
| 21 from StringIO import StringIO | |
| 22 | |
| 23 # Twisted Imports | |
| 24 from twisted import copyright | |
| 25 from twisted.internet import protocol | |
| 26 | |
| 27 # Some utility chars. | |
| 28 ESC = chr(27) # ESC for doing fanciness | |
| 29 BOLD_MODE_ON = ESC+"[1m" # turn bold on | |
| 30 BOLD_MODE_OFF= ESC+"[m" # no char attributes | |
| 31 | |
| 32 | |
| 33 # Characters gleaned from the various (and conflicting) RFCs. Not all of these
are correct. | |
| 34 | |
| 35 NULL = chr(0) # No operation. | |
| 36 LF = chr(10) # Moves the printer to the | |
| 37 # next print line, keeping the | |
| 38 # same horizontal position. | |
| 39 CR = chr(13) # Moves the printer to the left | |
| 40 # margin of the current line. | |
| 41 BEL = chr(7) # Produces an audible or | |
| 42 # visible signal (which does | |
| 43 # NOT move the print head). | |
| 44 BS = chr(8) # Moves the print head one | |
| 45 # character position towards | |
| 46 # the left margin. | |
| 47 HT = chr(9) # Moves the printer to the | |
| 48 # next horizontal tab stop. | |
| 49 # It remains unspecified how | |
| 50 # either party determines or | |
| 51 # establishes where such tab | |
| 52 # stops are located. | |
| 53 VT = chr(11) # Moves the printer to the | |
| 54 # next vertical tab stop. It | |
| 55 # remains unspecified how | |
| 56 # either party determines or | |
| 57 # establishes where such tab | |
| 58 # stops are located. | |
| 59 FF = chr(12) # Moves the printer to the top | |
| 60 # of the next page, keeping | |
| 61 # the same horizontal position. | |
| 62 SE = chr(240) # End of subnegotiation parameters. | |
| 63 NOP= chr(241) # No operation. | |
| 64 DM = chr(242) # "Data Mark": The data stream portion | |
| 65 # of a Synch. This should always be | |
| 66 # accompanied by a TCP Urgent | |
| 67 # notification. | |
| 68 BRK= chr(243) # NVT character Break. | |
| 69 IP = chr(244) # The function Interrupt Process. | |
| 70 AO = chr(245) # The function Abort Output | |
| 71 AYT= chr(246) # The function Are You There. | |
| 72 EC = chr(247) # The function Erase Character. | |
| 73 EL = chr(248) # The function Erase Line | |
| 74 GA = chr(249) # The Go Ahead signal. | |
| 75 SB = chr(250) # Indicates that what follows is | |
| 76 # subnegotiation of the indicated | |
| 77 # option. | |
| 78 WILL = chr(251) # Indicates the desire to begin | |
| 79 # performing, or confirmation that | |
| 80 # you are now performing, the | |
| 81 # indicated option. | |
| 82 WONT = chr(252) # Indicates the refusal to perform, | |
| 83 # or continue performing, the | |
| 84 # indicated option. | |
| 85 DO = chr(253) # Indicates the request that the | |
| 86 # other party perform, or | |
| 87 # confirmation that you are expecting | |
| 88 # the other party to perform, the | |
| 89 # indicated option. | |
| 90 DONT = chr(254) # Indicates the demand that the | |
| 91 # other party stop performing, | |
| 92 # or confirmation that you are no | |
| 93 # longer expecting the other party | |
| 94 # to perform, the indicated option. | |
| 95 IAC = chr(255) # Data Byte 255. | |
| 96 | |
| 97 # features | |
| 98 | |
| 99 ECHO = chr(1) # User-to-Server: Asks the server to send | |
| 100 # Echos of the transmitted data. | |
| 101 | |
| 102 # Server-to User: States that the server is | |
| 103 # sending echos of the transmitted data. | |
| 104 # Sent only as a reply to ECHO or NO ECHO. | |
| 105 | |
| 106 SUPGA = chr(3) # Supress Go Ahead...? "Modern" telnet servers | |
| 107 # are supposed to do this. | |
| 108 | |
| 109 LINEMODE = chr(34) # I don't care that Jon Postel is dead. | |
| 110 | |
| 111 HIDE = chr(133) # The intention is that a server will send | |
| 112 # this signal to a user system which is | |
| 113 # echoing locally (to the user) when the user | |
| 114 # is about to type something secret (e.g. a | |
| 115 # password). In this case, the user system | |
| 116 # is to suppress local echoing or overprint | |
| 117 # the input (or something) until the server | |
| 118 # sends a NOECHO signal. In situations where | |
| 119 # the user system is not echoing locally, | |
| 120 # this signal must not be sent by the server. | |
| 121 | |
| 122 | |
| 123 NOECHO= chr(131) # User-to-Server: Asks the server not to | |
| 124 # return Echos of the transmitted data. | |
| 125 # | |
| 126 # Server-to-User: States that the server is | |
| 127 # not sending echos of the transmitted data. | |
| 128 # Sent only as a reply to ECHO or NO ECHO, | |
| 129 # or to end the hide your input. | |
| 130 | |
| 131 | |
| 132 | |
| 133 iacBytes = { | |
| 134 DO: 'DO', | |
| 135 DONT: 'DONT', | |
| 136 WILL: 'WILL', | |
| 137 WONT: 'WONT', | |
| 138 IP: 'IP' | |
| 139 } | |
| 140 | |
| 141 def multireplace(st, dct): | |
| 142 for k, v in dct.items(): | |
| 143 st = st.replace(k, v) | |
| 144 return st | |
| 145 | |
| 146 class Telnet(protocol.Protocol): | |
| 147 """I am a Protocol for handling Telnet connections. I have two | |
| 148 sets of special methods, telnet_* and iac_*. | |
| 149 | |
| 150 telnet_* methods get called on every line sent to me. The method | |
| 151 to call is decided by the current mode. The initial mode is 'User'; | |
| 152 this means that telnet_User is the first telnet_* method to be called. | |
| 153 All telnet_* methods should return a string which specifies the mode | |
| 154 to go into next; thus dictating which telnet_* method to call next. | |
| 155 For example, the default telnet_User method returns 'Password' to go | |
| 156 into Password mode, and the default telnet_Password method returns | |
| 157 'Command' to go into Command mode. | |
| 158 | |
| 159 The iac_* methods are less-used; they are called when an IAC telnet | |
| 160 byte is received. You can define iac_DO, iac_DONT, iac_WILL, iac_WONT, | |
| 161 and iac_IP methods to do what you want when one of these bytes is | |
| 162 received.""" | |
| 163 | |
| 164 | |
| 165 gotIAC = 0 | |
| 166 iacByte = None | |
| 167 lastLine = None | |
| 168 buffer = '' | |
| 169 echo = 0 | |
| 170 delimiters = ['\r\n', '\r\000'] | |
| 171 mode = "User" | |
| 172 | |
| 173 def write(self, data): | |
| 174 """Send the given data over my transport.""" | |
| 175 self.transport.write(data) | |
| 176 | |
| 177 | |
| 178 def connectionMade(self): | |
| 179 """I will write a welcomeMessage and loginPrompt to the client.""" | |
| 180 self.write(self.welcomeMessage() + self.loginPrompt()) | |
| 181 | |
| 182 def welcomeMessage(self): | |
| 183 """Override me to return a string which will be sent to the client | |
| 184 before login.""" | |
| 185 x = self.factory.__class__ | |
| 186 return ("\r\n" + x.__module__ + '.' + x.__name__ + | |
| 187 '\r\nTwisted %s\r\n' % copyright.version | |
| 188 ) | |
| 189 | |
| 190 def loginPrompt(self): | |
| 191 """Override me to return a 'login:'-type prompt.""" | |
| 192 return "username: " | |
| 193 | |
| 194 def iacSBchunk(self, chunk): | |
| 195 pass | |
| 196 | |
| 197 def iac_DO(self, feature): | |
| 198 pass | |
| 199 | |
| 200 def iac_DONT(self, feature): | |
| 201 pass | |
| 202 | |
| 203 def iac_WILL(self, feature): | |
| 204 pass | |
| 205 | |
| 206 def iac_WONT(self, feature): | |
| 207 pass | |
| 208 | |
| 209 def iac_IP(self, feature): | |
| 210 pass | |
| 211 | |
| 212 def processLine(self, line): | |
| 213 """I call a method that looks like 'telnet_*' where '*' is filled | |
| 214 in by the current mode. telnet_* methods should return a string which | |
| 215 will become the new mode. If None is returned, the mode will not change
. | |
| 216 """ | |
| 217 mode = getattr(self, "telnet_"+self.mode)(line) | |
| 218 if mode is not None: | |
| 219 self.mode = mode | |
| 220 | |
| 221 def telnet_User(self, user): | |
| 222 """I take a username, set it to the 'self.username' attribute, | |
| 223 print out a password prompt, and switch to 'Password' mode. If | |
| 224 you want to do something else when the username is received (ie, | |
| 225 create a new user if the user doesn't exist), override me.""" | |
| 226 self.username = user | |
| 227 self.write(IAC+WILL+ECHO+"password: ") | |
| 228 return "Password" | |
| 229 | |
| 230 def telnet_Password(self, paswd): | |
| 231 """I accept a password as an argument, and check it with the | |
| 232 checkUserAndPass method. If the login is successful, I call | |
| 233 loggedIn().""" | |
| 234 self.write(IAC+WONT+ECHO+"*****\r\n") | |
| 235 try: | |
| 236 checked = self.checkUserAndPass(self.username, paswd) | |
| 237 except: | |
| 238 return "Done" | |
| 239 if not checked: | |
| 240 return "Done" | |
| 241 self.loggedIn() | |
| 242 return "Command" | |
| 243 | |
| 244 def telnet_Command(self, cmd): | |
| 245 """The default 'command processing' mode. You probably want to | |
| 246 override me.""" | |
| 247 return "Command" | |
| 248 | |
| 249 def processChunk(self, chunk): | |
| 250 """I take a chunk of data and delegate out to telnet_* methods | |
| 251 by way of processLine. If the current mode is 'Done', I'll close | |
| 252 the connection. """ | |
| 253 self.buffer = self.buffer + chunk | |
| 254 | |
| 255 #yech. | |
| 256 for delim in self.delimiters: | |
| 257 idx = self.buffer.find(delim) | |
| 258 if idx != -1: | |
| 259 break | |
| 260 | |
| 261 while idx != -1: | |
| 262 buf, self.buffer = self.buffer[:idx], self.buffer[idx+2:] | |
| 263 self.processLine(buf) | |
| 264 if self.mode == 'Done': | |
| 265 self.transport.loseConnection() | |
| 266 | |
| 267 for delim in self.delimiters: | |
| 268 idx = self.buffer.find(delim) | |
| 269 if idx != -1: | |
| 270 break | |
| 271 | |
| 272 def dataReceived(self, data): | |
| 273 chunk = StringIO() | |
| 274 # silly little IAC state-machine | |
| 275 for char in data: | |
| 276 if self.gotIAC: | |
| 277 # working on an IAC request state | |
| 278 if self.iacByte: | |
| 279 # we're in SB mode, getting a chunk | |
| 280 if self.iacByte == SB: | |
| 281 if char == SE: | |
| 282 self.iacSBchunk(chunk.getvalue()) | |
| 283 chunk = StringIO() | |
| 284 del self.iacByte | |
| 285 del self.gotIAC | |
| 286 else: | |
| 287 chunk.write(char) | |
| 288 else: | |
| 289 # got all I need to know state | |
| 290 try: | |
| 291 getattr(self, 'iac_%s' % iacBytes[self.iacByte])(cha
r) | |
| 292 except KeyError: | |
| 293 pass | |
| 294 del self.iacByte | |
| 295 del self.gotIAC | |
| 296 else: | |
| 297 # got IAC, this is my W/W/D/D (or perhaps sb) | |
| 298 self.iacByte = char | |
| 299 elif char == IAC: | |
| 300 # Process what I've got so far before going into | |
| 301 # the IAC state; don't want to process characters | |
| 302 # in an inconsistent state with what they were | |
| 303 # received in. | |
| 304 c = chunk.getvalue() | |
| 305 if c: | |
| 306 why = self.processChunk(c) | |
| 307 if why: | |
| 308 return why | |
| 309 chunk = StringIO() | |
| 310 self.gotIAC = 1 | |
| 311 else: | |
| 312 chunk.write(char) | |
| 313 # chunks are of a relatively indeterminate size. | |
| 314 c = chunk.getvalue() | |
| 315 if c: | |
| 316 why = self.processChunk(c) | |
| 317 if why: | |
| 318 return why | |
| 319 | |
| 320 def loggedIn(self): | |
| 321 """Called after the user succesfully logged in. | |
| 322 | |
| 323 Override in subclasses. | |
| 324 """ | |
| 325 pass | |
| OLD | NEW |