telnet.py 38 KB

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