1# -*- test-case-name: twisted.conch.test.test_telnet -*- 2# Copyright (c) Twisted Matrix Laboratories. 3# See LICENSE for details. 4 5""" 6Telnet protocol implementation. 7 8@author: Jean-Paul Calderone 9""" 10 11import struct 12 13from zope.interface import implements 14 15from twisted.internet import protocol, interfaces as iinternet, defer 16from twisted.python import log 17 18MODE = chr(1) 19EDIT = 1 20TRAPSIG = 2 21MODE_ACK = 4 22SOFT_TAB = 8 23LIT_ECHO = 16 24 25# Characters gleaned from the various (and conflicting) RFCs. Not all of these are correct. 26 27NULL = chr(0) # No operation. 28BEL = chr(7) # Produces an audible or 29 # visible signal (which does 30 # NOT move the print head). 31BS = chr(8) # Moves the print head one 32 # character position towards 33 # the left margin. 34HT = chr(9) # Moves the printer to the 35 # next horizontal tab stop. 36 # It remains unspecified how 37 # either party determines or 38 # establishes where such tab 39 # stops are located. 40LF = chr(10) # Moves the printer to the 41 # next print line, keeping the 42 # same horizontal position. 43VT = chr(11) # Moves the printer to the 44 # next vertical tab stop. It 45 # remains unspecified how 46 # either party determines or 47 # establishes where such tab 48 # stops are located. 49FF = chr(12) # Moves the printer to the top 50 # of the next page, keeping 51 # the same horizontal position. 52CR = chr(13) # Moves the printer to the left 53 # margin of the current line. 54 55ECHO = chr(1) # User-to-Server: Asks the server to send 56 # Echos of the transmitted data. 57SGA = chr(3) # Suppress Go Ahead. Go Ahead is silly 58 # and most modern servers should suppress 59 # it. 60NAWS = chr(31) # Negotiate About Window Size. Indicate that 61 # information about the size of the terminal 62 # can be communicated. 63LINEMODE = chr(34) # Allow line buffering to be 64 # negotiated about. 65 66SE = chr(240) # End of subnegotiation parameters. 67NOP = chr(241) # No operation. 68DM = chr(242) # "Data Mark": The data stream portion 69 # of a Synch. This should always be 70 # accompanied by a TCP Urgent 71 # notification. 72BRK = chr(243) # NVT character Break. 73IP = chr(244) # The function Interrupt Process. 74AO = chr(245) # The function Abort Output 75AYT = chr(246) # The function Are You There. 76EC = chr(247) # The function Erase Character. 77EL = chr(248) # The function Erase Line 78GA = chr(249) # The Go Ahead signal. 79SB = chr(250) # Indicates that what follows is 80 # subnegotiation of the indicated 81 # option. 82WILL = chr(251) # Indicates the desire to begin 83 # performing, or confirmation that 84 # you are now performing, the 85 # indicated option. 86WONT = chr(252) # Indicates the refusal to perform, 87 # or continue performing, the 88 # indicated option. 89DO = chr(253) # Indicates the request that the 90 # other party perform, or 91 # confirmation that you are expecting 92 # the other party to perform, the 93 # indicated option. 94DONT = chr(254) # Indicates the demand that the 95 # other party stop performing, 96 # or confirmation that you are no 97 # longer expecting the other party 98 # to perform, the indicated option. 99IAC = chr(255) # Data Byte 255. Introduces a 100 # telnet command. 101 102LINEMODE_MODE = chr(1) 103LINEMODE_EDIT = chr(1) 104LINEMODE_TRAPSIG = chr(2) 105LINEMODE_MODE_ACK = chr(4) 106LINEMODE_SOFT_TAB = chr(8) 107LINEMODE_LIT_ECHO = chr(16) 108LINEMODE_FORWARDMASK = chr(2) 109LINEMODE_SLC = chr(3) 110LINEMODE_SLC_SYNCH = chr(1) 111LINEMODE_SLC_BRK = chr(2) 112LINEMODE_SLC_IP = chr(3) 113LINEMODE_SLC_AO = chr(4) 114LINEMODE_SLC_AYT = chr(5) 115LINEMODE_SLC_EOR = chr(6) 116LINEMODE_SLC_ABORT = chr(7) 117LINEMODE_SLC_EOF = chr(8) 118LINEMODE_SLC_SUSP = chr(9) 119LINEMODE_SLC_EC = chr(10) 120LINEMODE_SLC_EL = chr(11) 121 122LINEMODE_SLC_EW = chr(12) 123LINEMODE_SLC_RP = chr(13) 124LINEMODE_SLC_LNEXT = chr(14) 125LINEMODE_SLC_XON = chr(15) 126LINEMODE_SLC_XOFF = chr(16) 127LINEMODE_SLC_FORW1 = chr(17) 128LINEMODE_SLC_FORW2 = chr(18) 129LINEMODE_SLC_MCL = chr(19) 130LINEMODE_SLC_MCR = chr(20) 131LINEMODE_SLC_MCWL = chr(21) 132LINEMODE_SLC_MCWR = chr(22) 133LINEMODE_SLC_MCBOL = chr(23) 134LINEMODE_SLC_MCEOL = chr(24) 135LINEMODE_SLC_INSRT = chr(25) 136LINEMODE_SLC_OVER = chr(26) 137LINEMODE_SLC_ECR = chr(27) 138LINEMODE_SLC_EWR = chr(28) 139LINEMODE_SLC_EBOL = chr(29) 140LINEMODE_SLC_EEOL = chr(30) 141 142LINEMODE_SLC_DEFAULT = chr(3) 143LINEMODE_SLC_VALUE = chr(2) 144LINEMODE_SLC_CANTCHANGE = chr(1) 145LINEMODE_SLC_NOSUPPORT = chr(0) 146LINEMODE_SLC_LEVELBITS = chr(3) 147 148LINEMODE_SLC_ACK = chr(128) 149LINEMODE_SLC_FLUSHIN = chr(64) 150LINEMODE_SLC_FLUSHOUT = chr(32) 151LINEMODE_EOF = chr(236) 152LINEMODE_SUSP = chr(237) 153LINEMODE_ABORT = chr(238) 154 155class ITelnetProtocol(iinternet.IProtocol): 156 def unhandledCommand(command, argument): 157 """A command was received but not understood. 158 159 @param command: the command received. 160 @type command: C{str}, a single character. 161 @param argument: the argument to the received command. 162 @type argument: C{str}, a single character, or None if the command that 163 was unhandled does not provide an argument. 164 """ 165 166 def unhandledSubnegotiation(command, bytes): 167 """A subnegotiation command was received but not understood. 168 169 @param command: the command being subnegotiated. That is, the first 170 byte after the SB command. 171 @type command: C{str}, a single character. 172 @param bytes: all other bytes of the subneogation. That is, all but the 173 first bytes between SB and SE, with IAC un-escaping applied. 174 @type bytes: C{list} of C{str}, each a single character 175 """ 176 177 def enableLocal(option): 178 """Enable the given option locally. 179 180 This should enable the given option on this side of the 181 telnet connection and return True. If False is returned, 182 the option will be treated as still disabled and the peer 183 will be notified. 184 185 @param option: the option to be enabled. 186 @type option: C{str}, a single character. 187 """ 188 189 def enableRemote(option): 190 """Indicate whether the peer should be allowed to enable this option. 191 192 Returns True if the peer should be allowed to enable this option, 193 False otherwise. 194 195 @param option: the option to be enabled. 196 @type option: C{str}, a single character. 197 """ 198 199 def disableLocal(option): 200 """Disable the given option locally. 201 202 Unlike enableLocal, this method cannot fail. The option must be 203 disabled. 204 205 @param option: the option to be disabled. 206 @type option: C{str}, a single character. 207 """ 208 209 def disableRemote(option): 210 """Indicate that the peer has disabled this option. 211 212 @param option: the option to be disabled. 213 @type option: C{str}, a single character. 214 """ 215 216 217 218class ITelnetTransport(iinternet.ITransport): 219 def do(option): 220 """ 221 Indicate a desire for the peer to begin performing the given option. 222 223 Returns a Deferred that fires with True when the peer begins performing 224 the option, or fails with L{OptionRefused} when the peer refuses to 225 perform it. If the peer is already performing the given option, the 226 Deferred will fail with L{AlreadyEnabled}. If a negotiation regarding 227 this option is already in progress, the Deferred will fail with 228 L{AlreadyNegotiating}. 229 230 Note: It is currently possible that this Deferred will never fire, 231 if the peer never responds, or if the peer believes the option to 232 already be enabled. 233 """ 234 235 236 def dont(option): 237 """ 238 Indicate a desire for the peer to cease performing the given option. 239 240 Returns a Deferred that fires with True when the peer ceases performing 241 the option. If the peer is not performing the given option, the 242 Deferred will fail with L{AlreadyDisabled}. If negotiation regarding 243 this option is already in progress, the Deferred will fail with 244 L{AlreadyNegotiating}. 245 246 Note: It is currently possible that this Deferred will never fire, 247 if the peer never responds, or if the peer believes the option to 248 already be disabled. 249 """ 250 251 252 def will(option): 253 """ 254 Indicate our willingness to begin performing this option locally. 255 256 Returns a Deferred that fires with True when the peer agrees to allow us 257 to begin performing this option, or fails with L{OptionRefused} if the 258 peer refuses to allow us to begin performing it. If the option is 259 already enabled locally, the Deferred will fail with L{AlreadyEnabled}. 260 If negotiation regarding this option is already in progress, the 261 Deferred will fail with L{AlreadyNegotiating}. 262 263 Note: It is currently possible that this Deferred will never fire, 264 if the peer never responds, or if the peer believes the option to 265 already be enabled. 266 """ 267 268 269 def wont(option): 270 """ 271 Indicate that we will stop performing the given option. 272 273 Returns a Deferred that fires with True when the peer acknowledges 274 we have stopped performing this option. If the option is already 275 disabled locally, the Deferred will fail with L{AlreadyDisabled}. 276 If negotiation regarding this option is already in progress, 277 the Deferred will fail with L{AlreadyNegotiating}. 278 279 Note: It is currently possible that this Deferred will never fire, 280 if the peer never responds, or if the peer believes the option to 281 already be disabled. 282 """ 283 284 285 def requestNegotiation(about, bytes): 286 """ 287 Send a subnegotiation request. 288 289 @param about: A byte indicating the feature being negotiated. 290 @param bytes: Any number of bytes containing specific information 291 about the negotiation being requested. No values in this string 292 need to be escaped, as this function will escape any value which 293 requires it. 294 """ 295 296 297 298class TelnetError(Exception): 299 pass 300 301class NegotiationError(TelnetError): 302 def __str__(self): 303 return self.__class__.__module__ + '.' + self.__class__.__name__ + ':' + repr(self.args[0]) 304 305class OptionRefused(NegotiationError): 306 pass 307 308class AlreadyEnabled(NegotiationError): 309 pass 310 311class AlreadyDisabled(NegotiationError): 312 pass 313 314class AlreadyNegotiating(NegotiationError): 315 pass 316 317class TelnetProtocol(protocol.Protocol): 318 implements(ITelnetProtocol) 319 320 def unhandledCommand(self, command, argument): 321 pass 322 323 def unhandledSubnegotiation(self, command, bytes): 324 pass 325 326 def enableLocal(self, option): 327 pass 328 329 def enableRemote(self, option): 330 pass 331 332 def disableLocal(self, option): 333 pass 334 335 def disableRemote(self, option): 336 pass 337 338 339class Telnet(protocol.Protocol): 340 """ 341 @ivar commandMap: A mapping of bytes to callables. When a 342 telnet command is received, the command byte (the first byte 343 after IAC) is looked up in this dictionary. If a callable is 344 found, it is invoked with the argument of the command, or None 345 if the command takes no argument. Values should be added to 346 this dictionary if commands wish to be handled. By default, 347 only WILL, WONT, DO, and DONT are handled. These should not 348 be overridden, as this class handles them correctly and 349 provides an API for interacting with them. 350 351 @ivar negotiationMap: A mapping of bytes to callables. When 352 a subnegotiation command is received, the command byte (the 353 first byte after SB) is looked up in this dictionary. If 354 a callable is found, it is invoked with the argument of the 355 subnegotiation. Values should be added to this dictionary if 356 subnegotiations are to be handled. By default, no values are 357 handled. 358 359 @ivar options: A mapping of option bytes to their current 360 state. This state is likely of little use to user code. 361 Changes should not be made to it. 362 363 @ivar state: A string indicating the current parse state. It 364 can take on the values "data", "escaped", "command", "newline", 365 "subnegotiation", and "subnegotiation-escaped". Changes 366 should not be made to it. 367 368 @ivar transport: This protocol's transport object. 369 """ 370 371 # One of a lot of things 372 state = 'data' 373 374 def __init__(self): 375 self.options = {} 376 self.negotiationMap = {} 377 self.commandMap = { 378 WILL: self.telnet_WILL, 379 WONT: self.telnet_WONT, 380 DO: self.telnet_DO, 381 DONT: self.telnet_DONT} 382 383 def _write(self, bytes): 384 self.transport.write(bytes) 385 386 class _OptionState: 387 """ 388 Represents the state of an option on both sides of a telnet 389 connection. 390 391 @ivar us: The state of the option on this side of the connection. 392 393 @ivar him: The state of the option on the other side of the 394 connection. 395 """ 396 class _Perspective: 397 """ 398 Represents the state of an option on side of the telnet 399 connection. Some options can be enabled on a particular side of 400 the connection (RFC 1073 for example: only the client can have 401 NAWS enabled). Other options can be enabled on either or both 402 sides (such as RFC 1372: each side can have its own flow control 403 state). 404 405 @ivar state: C{'yes'} or C{'no'} indicating whether or not this 406 option is enabled on one side of the connection. 407 408 @ivar negotiating: A boolean tracking whether negotiation about 409 this option is in progress. 410 411 @ivar onResult: When negotiation about this option has been 412 initiated by this side of the connection, a L{Deferred} 413 which will fire with the result of the negotiation. C{None} 414 at other times. 415 """ 416 state = 'no' 417 negotiating = False 418 onResult = None 419 420 def __str__(self): 421 return self.state + ('*' * self.negotiating) 422 423 def __init__(self): 424 self.us = self._Perspective() 425 self.him = self._Perspective() 426 427 def __repr__(self): 428 return '<_OptionState us=%s him=%s>' % (self.us, self.him) 429 430 def getOptionState(self, opt): 431 return self.options.setdefault(opt, self._OptionState()) 432 433 def _do(self, option): 434 self._write(IAC + DO + option) 435 436 def _dont(self, option): 437 self._write(IAC + DONT + option) 438 439 def _will(self, option): 440 self._write(IAC + WILL + option) 441 442 def _wont(self, option): 443 self._write(IAC + WONT + option) 444 445 def will(self, option): 446 """Indicate our willingness to enable an option. 447 """ 448 s = self.getOptionState(option) 449 if s.us.negotiating or s.him.negotiating: 450 return defer.fail(AlreadyNegotiating(option)) 451 elif s.us.state == 'yes': 452 return defer.fail(AlreadyEnabled(option)) 453 else: 454 s.us.negotiating = True 455 s.us.onResult = d = defer.Deferred() 456 self._will(option) 457 return d 458 459 def wont(self, option): 460 """Indicate we are not willing to enable an option. 461 """ 462 s = self.getOptionState(option) 463 if s.us.negotiating or s.him.negotiating: 464 return defer.fail(AlreadyNegotiating(option)) 465 elif s.us.state == 'no': 466 return defer.fail(AlreadyDisabled(option)) 467 else: 468 s.us.negotiating = True 469 s.us.onResult = d = defer.Deferred() 470 self._wont(option) 471 return d 472 473 def do(self, option): 474 s = self.getOptionState(option) 475 if s.us.negotiating or s.him.negotiating: 476 return defer.fail(AlreadyNegotiating(option)) 477 elif s.him.state == 'yes': 478 return defer.fail(AlreadyEnabled(option)) 479 else: 480 s.him.negotiating = True 481 s.him.onResult = d = defer.Deferred() 482 self._do(option) 483 return d 484 485 def dont(self, option): 486 s = self.getOptionState(option) 487 if s.us.negotiating or s.him.negotiating: 488 return defer.fail(AlreadyNegotiating(option)) 489 elif s.him.state == 'no': 490 return defer.fail(AlreadyDisabled(option)) 491 else: 492 s.him.negotiating = True 493 s.him.onResult = d = defer.Deferred() 494 self._dont(option) 495 return d 496 497 498 def requestNegotiation(self, about, bytes): 499 """ 500 Send a negotiation message for the option C{about} with C{bytes} as the 501 payload. 502 503 @see: L{ITelnetTransport.requestNegotiation} 504 """ 505 bytes = bytes.replace(IAC, IAC * 2) 506 self._write(IAC + SB + about + bytes + IAC + SE) 507 508 509 def dataReceived(self, data): 510 appDataBuffer = [] 511 512 for b in data: 513 if self.state == 'data': 514 if b == IAC: 515 self.state = 'escaped' 516 elif b == '\r': 517 self.state = 'newline' 518 else: 519 appDataBuffer.append(b) 520 elif self.state == 'escaped': 521 if b == IAC: 522 appDataBuffer.append(b) 523 self.state = 'data' 524 elif b == SB: 525 self.state = 'subnegotiation' 526 self.commands = [] 527 elif b in (NOP, DM, BRK, IP, AO, AYT, EC, EL, GA): 528 self.state = 'data' 529 if appDataBuffer: 530 self.applicationDataReceived(''.join(appDataBuffer)) 531 del appDataBuffer[:] 532 self.commandReceived(b, None) 533 elif b in (WILL, WONT, DO, DONT): 534 self.state = 'command' 535 self.command = b 536 else: 537 raise ValueError("Stumped", b) 538 elif self.state == 'command': 539 self.state = 'data' 540 command = self.command 541 del self.command 542 if appDataBuffer: 543 self.applicationDataReceived(''.join(appDataBuffer)) 544 del appDataBuffer[:] 545 self.commandReceived(command, b) 546 elif self.state == 'newline': 547 self.state = 'data' 548 if b == '\n': 549 appDataBuffer.append('\n') 550 elif b == '\0': 551 appDataBuffer.append('\r') 552 elif b == IAC: 553 # IAC isn't really allowed after \r, according to the 554 # RFC, but handling it this way is less surprising than 555 # delivering the IAC to the app as application data. 556 # The purpose of the restriction is to allow terminals 557 # to unambiguously interpret the behavior of the CR 558 # after reading only one more byte. CR LF is supposed 559 # to mean one thing (cursor to next line, first column), 560 # CR NUL another (cursor to first column). Absent the 561 # NUL, it still makes sense to interpret this as CR and 562 # then apply all the usual interpretation to the IAC. 563 appDataBuffer.append('\r') 564 self.state = 'escaped' 565 else: 566 appDataBuffer.append('\r' + b) 567 elif self.state == 'subnegotiation': 568 if b == IAC: 569 self.state = 'subnegotiation-escaped' 570 else: 571 self.commands.append(b) 572 elif self.state == 'subnegotiation-escaped': 573 if b == SE: 574 self.state = 'data' 575 commands = self.commands 576 del self.commands 577 if appDataBuffer: 578 self.applicationDataReceived(''.join(appDataBuffer)) 579 del appDataBuffer[:] 580 self.negotiate(commands) 581 else: 582 self.state = 'subnegotiation' 583 self.commands.append(b) 584 else: 585 raise ValueError("How'd you do this?") 586 587 if appDataBuffer: 588 self.applicationDataReceived(''.join(appDataBuffer)) 589 590 591 def connectionLost(self, reason): 592 for state in self.options.values(): 593 if state.us.onResult is not None: 594 d = state.us.onResult 595 state.us.onResult = None 596 d.errback(reason) 597 if state.him.onResult is not None: 598 d = state.him.onResult 599 state.him.onResult = None 600 d.errback(reason) 601 602 def applicationDataReceived(self, bytes): 603 """Called with application-level data. 604 """ 605 606 def unhandledCommand(self, command, argument): 607 """Called for commands for which no handler is installed. 608 """ 609 610 def commandReceived(self, command, argument): 611 cmdFunc = self.commandMap.get(command) 612 if cmdFunc is None: 613 self.unhandledCommand(command, argument) 614 else: 615 cmdFunc(argument) 616 617 def unhandledSubnegotiation(self, command, bytes): 618 """Called for subnegotiations for which no handler is installed. 619 """ 620 621 def negotiate(self, bytes): 622 command, bytes = bytes[0], bytes[1:] 623 cmdFunc = self.negotiationMap.get(command) 624 if cmdFunc is None: 625 self.unhandledSubnegotiation(command, bytes) 626 else: 627 cmdFunc(bytes) 628 629 def telnet_WILL(self, option): 630 s = self.getOptionState(option) 631 self.willMap[s.him.state, s.him.negotiating](self, s, option) 632 633 def will_no_false(self, state, option): 634 # He is unilaterally offering to enable an option. 635 if self.enableRemote(option): 636 state.him.state = 'yes' 637 self._do(option) 638 else: 639 self._dont(option) 640 641 def will_no_true(self, state, option): 642 # Peer agreed to enable an option in response to our request. 643 state.him.state = 'yes' 644 state.him.negotiating = False 645 d = state.him.onResult 646 state.him.onResult = None 647 d.callback(True) 648 assert self.enableRemote(option), "enableRemote must return True in this context (for option %r)" % (option,) 649 650 def will_yes_false(self, state, option): 651 # He is unilaterally offering to enable an already-enabled option. 652 # Ignore this. 653 pass 654 655 def will_yes_true(self, state, option): 656 # This is a bogus state. It is here for completeness. It will 657 # never be entered. 658 assert False, "will_yes_true can never be entered, but was called with %r, %r" % (state, option) 659 660 willMap = {('no', False): will_no_false, ('no', True): will_no_true, 661 ('yes', False): will_yes_false, ('yes', True): will_yes_true} 662 663 def telnet_WONT(self, option): 664 s = self.getOptionState(option) 665 self.wontMap[s.him.state, s.him.negotiating](self, s, option) 666 667 def wont_no_false(self, state, option): 668 # He is unilaterally demanding that an already-disabled option be/remain disabled. 669 # Ignore this (although we could record it and refuse subsequent enable attempts 670 # from our side - he can always refuse them again though, so we won't) 671 pass 672 673 def wont_no_true(self, state, option): 674 # Peer refused to enable an option in response to our request. 675 state.him.negotiating = False 676 d = state.him.onResult 677 state.him.onResult = None 678 d.errback(OptionRefused(option)) 679 680 def wont_yes_false(self, state, option): 681 # Peer is unilaterally demanding that an option be disabled. 682 state.him.state = 'no' 683 self.disableRemote(option) 684 self._dont(option) 685 686 def wont_yes_true(self, state, option): 687 # Peer agreed to disable an option at our request. 688 state.him.state = 'no' 689 state.him.negotiating = False 690 d = state.him.onResult 691 state.him.onResult = None 692 d.callback(True) 693 self.disableRemote(option) 694 695 wontMap = {('no', False): wont_no_false, ('no', True): wont_no_true, 696 ('yes', False): wont_yes_false, ('yes', True): wont_yes_true} 697 698 def telnet_DO(self, option): 699 s = self.getOptionState(option) 700 self.doMap[s.us.state, s.us.negotiating](self, s, option) 701 702 def do_no_false(self, state, option): 703 # Peer is unilaterally requesting that we enable an option. 704 if self.enableLocal(option): 705 state.us.state = 'yes' 706 self._will(option) 707 else: 708 self._wont(option) 709 710 def do_no_true(self, state, option): 711 # Peer agreed to allow us to enable an option at our request. 712 state.us.state = 'yes' 713 state.us.negotiating = False 714 d = state.us.onResult 715 state.us.onResult = None 716 d.callback(True) 717 self.enableLocal(option) 718 719 def do_yes_false(self, state, option): 720 # Peer is unilaterally requesting us to enable an already-enabled option. 721 # Ignore this. 722 pass 723 724 def do_yes_true(self, state, option): 725 # This is a bogus state. It is here for completeness. It will never be 726 # entered. 727 assert False, "do_yes_true can never be entered, but was called with %r, %r" % (state, option) 728 729 doMap = {('no', False): do_no_false, ('no', True): do_no_true, 730 ('yes', False): do_yes_false, ('yes', True): do_yes_true} 731 732 def telnet_DONT(self, option): 733 s = self.getOptionState(option) 734 self.dontMap[s.us.state, s.us.negotiating](self, s, option) 735 736 def dont_no_false(self, state, option): 737 # Peer is unilaterally demanding us to disable an already-disabled option. 738 # Ignore this. 739 pass 740 741 def dont_no_true(self, state, option): 742 # Offered option was refused. Fail the Deferred returned by the 743 # previous will() call. 744 state.us.negotiating = False 745 d = state.us.onResult 746 state.us.onResult = None 747 d.errback(OptionRefused(option)) 748 749 def dont_yes_false(self, state, option): 750 # Peer is unilaterally demanding we disable an option. 751 state.us.state = 'no' 752 self.disableLocal(option) 753 self._wont(option) 754 755 def dont_yes_true(self, state, option): 756 # Peer acknowledged our notice that we will disable an option. 757 state.us.state = 'no' 758 state.us.negotiating = False 759 d = state.us.onResult 760 state.us.onResult = None 761 d.callback(True) 762 self.disableLocal(option) 763 764 dontMap = {('no', False): dont_no_false, ('no', True): dont_no_true, 765 ('yes', False): dont_yes_false, ('yes', True): dont_yes_true} 766 767 def enableLocal(self, option): 768 """ 769 Reject all attempts to enable options. 770 """ 771 return False 772 773 774 def enableRemote(self, option): 775 """ 776 Reject all attempts to enable options. 777 """ 778 return False 779 780 781 def disableLocal(self, option): 782 """ 783 Signal a programming error by raising an exception. 784 785 L{enableLocal} must return true for the given value of C{option} in 786 order for this method to be called. If a subclass of L{Telnet} 787 overrides enableLocal to allow certain options to be enabled, it must 788 also override disableLocal to disable those options. 789 790 @raise NotImplementedError: Always raised. 791 """ 792 raise NotImplementedError( 793 "Don't know how to disable local telnet option %r" % (option,)) 794 795 796 def disableRemote(self, option): 797 """ 798 Signal a programming error by raising an exception. 799 800 L{enableRemote} must return true for the given value of C{option} in 801 order for this method to be called. If a subclass of L{Telnet} 802 overrides enableRemote to allow certain options to be enabled, it must 803 also override disableRemote tto disable those options. 804 805 @raise NotImplementedError: Always raised. 806 """ 807 raise NotImplementedError( 808 "Don't know how to disable remote telnet option %r" % (option,)) 809 810 811 812class ProtocolTransportMixin: 813 def write(self, bytes): 814 self.transport.write(bytes.replace('\n', '\r\n')) 815 816 def writeSequence(self, seq): 817 self.transport.writeSequence(seq) 818 819 def loseConnection(self): 820 self.transport.loseConnection() 821 822 def getHost(self): 823 return self.transport.getHost() 824 825 def getPeer(self): 826 return self.transport.getPeer() 827 828class TelnetTransport(Telnet, ProtocolTransportMixin): 829 """ 830 @ivar protocol: An instance of the protocol to which this 831 transport is connected, or None before the connection is 832 established and after it is lost. 833 834 @ivar protocolFactory: A callable which returns protocol instances 835 which provide L{ITelnetProtocol}. This will be invoked when a 836 connection is established. It is passed *protocolArgs and 837 **protocolKwArgs. 838 839 @ivar protocolArgs: A tuple of additional arguments to 840 pass to protocolFactory. 841 842 @ivar protocolKwArgs: A dictionary of additional arguments 843 to pass to protocolFactory. 844 """ 845 846 disconnecting = False 847 848 protocolFactory = None 849 protocol = None 850 851 def __init__(self, protocolFactory=None, *a, **kw): 852 Telnet.__init__(self) 853 if protocolFactory is not None: 854 self.protocolFactory = protocolFactory 855 self.protocolArgs = a 856 self.protocolKwArgs = kw 857 858 def connectionMade(self): 859 if self.protocolFactory is not None: 860 self.protocol = self.protocolFactory(*self.protocolArgs, **self.protocolKwArgs) 861 assert ITelnetProtocol.providedBy(self.protocol) 862 try: 863 factory = self.factory 864 except AttributeError: 865 pass 866 else: 867 self.protocol.factory = factory 868 self.protocol.makeConnection(self) 869 870 def connectionLost(self, reason): 871 Telnet.connectionLost(self, reason) 872 if self.protocol is not None: 873 try: 874 self.protocol.connectionLost(reason) 875 finally: 876 del self.protocol 877 878 def enableLocal(self, option): 879 return self.protocol.enableLocal(option) 880 881 def enableRemote(self, option): 882 return self.protocol.enableRemote(option) 883 884 def disableLocal(self, option): 885 return self.protocol.disableLocal(option) 886 887 def disableRemote(self, option): 888 return self.protocol.disableRemote(option) 889 890 def unhandledSubnegotiation(self, command, bytes): 891 self.protocol.unhandledSubnegotiation(command, bytes) 892 893 def unhandledCommand(self, command, argument): 894 self.protocol.unhandledCommand(command, argument) 895 896 def applicationDataReceived(self, bytes): 897 self.protocol.dataReceived(bytes) 898 899 def write(self, data): 900 ProtocolTransportMixin.write(self, data.replace('\xff','\xff\xff')) 901 902 903class TelnetBootstrapProtocol(TelnetProtocol, ProtocolTransportMixin): 904 implements() 905 906 protocol = None 907 908 def __init__(self, protocolFactory, *args, **kw): 909 self.protocolFactory = protocolFactory 910 self.protocolArgs = args 911 self.protocolKwArgs = kw 912 913 def connectionMade(self): 914 self.transport.negotiationMap[NAWS] = self.telnet_NAWS 915 self.transport.negotiationMap[LINEMODE] = self.telnet_LINEMODE 916 917 for opt in (LINEMODE, NAWS, SGA): 918 self.transport.do(opt).addErrback(log.err) 919 for opt in (ECHO,): 920 self.transport.will(opt).addErrback(log.err) 921 922 self.protocol = self.protocolFactory(*self.protocolArgs, **self.protocolKwArgs) 923 924 try: 925 factory = self.factory 926 except AttributeError: 927 pass 928 else: 929 self.protocol.factory = factory 930 931 self.protocol.makeConnection(self) 932 933 def connectionLost(self, reason): 934 if self.protocol is not None: 935 try: 936 self.protocol.connectionLost(reason) 937 finally: 938 del self.protocol 939 940 def dataReceived(self, data): 941 self.protocol.dataReceived(data) 942 943 def enableLocal(self, opt): 944 if opt == ECHO: 945 return True 946 elif opt == SGA: 947 return True 948 else: 949 return False 950 951 def enableRemote(self, opt): 952 if opt == LINEMODE: 953 self.transport.requestNegotiation(LINEMODE, MODE + chr(TRAPSIG)) 954 return True 955 elif opt == NAWS: 956 return True 957 elif opt == SGA: 958 return True 959 else: 960 return False 961 962 def telnet_NAWS(self, bytes): 963 # NAWS is client -> server *only*. self.protocol will 964 # therefore be an ITerminalTransport, the `.protocol' 965 # attribute of which will be an ITerminalProtocol. Maybe. 966 # You know what, XXX TODO clean this up. 967 if len(bytes) == 4: 968 width, height = struct.unpack('!HH', ''.join(bytes)) 969 self.protocol.terminalProtocol.terminalSize(width, height) 970 else: 971 log.msg("Wrong number of NAWS bytes") 972 973 974 linemodeSubcommands = { 975 LINEMODE_SLC: 'SLC'} 976 def telnet_LINEMODE(self, bytes): 977 revmap = {} 978 linemodeSubcommand = bytes[0] 979 if 0: 980 # XXX TODO: This should be enabled to parse linemode subnegotiation. 981 getattr(self, 'linemode_' + self.linemodeSubcommands[linemodeSubcommand])(bytes[1:]) 982 983 def linemode_SLC(self, bytes): 984 chunks = zip(*[iter(bytes)]*3) 985 for slcFunction, slcValue, slcWhat in chunks: 986 # Later, we should parse stuff. 987 'SLC', ord(slcFunction), ord(slcValue), ord(slcWhat) 988 989from twisted.protocols import basic 990 991class StatefulTelnetProtocol(basic.LineReceiver, TelnetProtocol): 992 delimiter = '\n' 993 994 state = 'Discard' 995 996 def connectionLost(self, reason): 997 basic.LineReceiver.connectionLost(self, reason) 998 TelnetProtocol.connectionLost(self, reason) 999 1000 def lineReceived(self, line): 1001 oldState = self.state 1002 newState = getattr(self, "telnet_" + oldState)(line) 1003 if newState is not None: 1004 if self.state == oldState: 1005 self.state = newState 1006 else: 1007 log.msg("Warning: state changed and new state returned") 1008 1009 def telnet_Discard(self, line): 1010 pass 1011 1012from twisted.cred import credentials 1013 1014class AuthenticatingTelnetProtocol(StatefulTelnetProtocol): 1015 """A protocol which prompts for credentials and attempts to authenticate them. 1016 1017 Username and password prompts are given (the password is obscured). When the 1018 information is collected, it is passed to a portal and an avatar implementing 1019 L{ITelnetProtocol} is requested. If an avatar is returned, it connected to this 1020 protocol's transport, and this protocol's transport is connected to it. 1021 Otherwise, the user is re-prompted for credentials. 1022 """ 1023 1024 state = "User" 1025 protocol = None 1026 1027 def __init__(self, portal): 1028 self.portal = portal 1029 1030 def connectionMade(self): 1031 self.transport.write("Username: ") 1032 1033 def connectionLost(self, reason): 1034 StatefulTelnetProtocol.connectionLost(self, reason) 1035 if self.protocol is not None: 1036 try: 1037 self.protocol.connectionLost(reason) 1038 self.logout() 1039 finally: 1040 del self.protocol, self.logout 1041 1042 def telnet_User(self, line): 1043 self.username = line 1044 self.transport.will(ECHO) 1045 self.transport.write("Password: ") 1046 return 'Password' 1047 1048 def telnet_Password(self, line): 1049 username, password = self.username, line 1050 del self.username 1051 def login(ignored): 1052 creds = credentials.UsernamePassword(username, password) 1053 d = self.portal.login(creds, None, ITelnetProtocol) 1054 d.addCallback(self._cbLogin) 1055 d.addErrback(self._ebLogin) 1056 self.transport.wont(ECHO).addCallback(login) 1057 return 'Discard' 1058 1059 def _cbLogin(self, ial): 1060 interface, protocol, logout = ial 1061 assert interface is ITelnetProtocol 1062 self.protocol = protocol 1063 self.logout = logout 1064 self.state = 'Command' 1065 1066 protocol.makeConnection(self.transport) 1067 self.transport.protocol = protocol 1068 1069 def _ebLogin(self, failure): 1070 self.transport.write("\nAuthentication failed\n") 1071 self.transport.write("Username: ") 1072 self.state = "User" 1073 1074__all__ = [ 1075 # Exceptions 1076 'TelnetError', 'NegotiationError', 'OptionRefused', 1077 'AlreadyNegotiating', 'AlreadyEnabled', 'AlreadyDisabled', 1078 1079 # Interfaces 1080 'ITelnetProtocol', 'ITelnetTransport', 1081 1082 # Other stuff, protocols, etc. 1083 'Telnet', 'TelnetProtocol', 'TelnetTransport', 1084 'TelnetBootstrapProtocol', 1085 1086 ] 1087