Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

telnet.py 37KB

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