rfc1143.cr 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. require_relative 'telnet/codes'
  2. # rfc1143 state machine for telnet protocol
  3. # Thehandle_ functions handle input,
  4. # the send_ functions are for sending negotiations
  5. class RFC1143
  6. include Telnet::Codes
  7. attr_reader :telopt
  8. attr_reader :us
  9. attr_reader :him
  10. attr_reader :agree
  11. def initialize(to, u, h, a)
  12. @telopt = to
  13. @us = u
  14. @him = h
  15. @agree = a
  16. end
  17. =begin
  18. EXAMPLE STATE MACHINE
  19. FOR THE Q METHOD OF IMPLEMENTING TELNET OPTION NEGOTIATION
  20. There are two sides, we (us) and he (him). We keep four
  21. variables:
  22. us: state of option on our side (NO/WANTNO/WANTYES/YES)
  23. usq: a queue bit (EMPTY/OPPOSITE) if us is WANTNO or WANTYES
  24. him: state of option on his side
  25. himq: a queue bit if him is WANTNO or WANTYES
  26. An option is enabled if and only if its state is YES. Note that
  27. us/usq and him/himq could be combined into two six-choice states.
  28. "Error" below means that producing diagnostic information may be a
  29. good idea, though it isn't required.
  30. Upon receipt of WILL, we choose based upon him and himq:
  31. NO If we agree that he should enable, him=YES, send
  32. DO; otherwise, send DONT.
  33. YES Ignore.
  34. WANTNO EMPTY Error: DONT answered by WILL. him=NO.
  35. OPPOSITE Error: DONT answered by WILL. him=YES*,
  36. himq=EMPTY.
  37. WANTYES EMPTY him=YES.
  38. OPPOSITE him=WANTNO, himq=EMPTY, send DONT.
  39. * This behavior is debatable; DONT will never be answered by WILL
  40. over a reliable connection between TELNETs compliant with this
  41. RFC, so this was chosen (1) not to generate further messages,
  42. because if we know we're dealing with a noncompliant TELNET we
  43. shouldn't trust it to be sensible; (2) to empty the queue
  44. sensibly.
  45. =end
  46. def handle_will
  47. case @us
  48. when :no
  49. if @agree
  50. return TELNET_DO, @telopt
  51. else
  52. return TELNET_DONT, @telopt
  53. end
  54. when :yes
  55. # ignore
  56. return nil, nil
  57. when :wantno
  58. @him = :no
  59. return :error, "DONT answered by WILL"
  60. when :wantno_opposite
  61. @him = :yes
  62. return :error, "DONT answered by WILL"
  63. when :wantyes
  64. @him = :yes
  65. return nil, nil
  66. when :wantyes_opposite
  67. @him = :wantno
  68. return TELNET_DONT, @telopt
  69. end
  70. end
  71. =begin
  72. Upon receipt of WONT, we choose based upon him and himq:
  73. NO Ignore.
  74. YES him=NO, send DONT.
  75. WANTNO EMPTY him=NO.
  76. OPPOSITE him=WANTYES, himq=NONE, send DO.
  77. WANTYES EMPTY him=NO.*
  78. OPPOSITE him=NO, himq=NONE.**
  79. * Here is the only spot a length-two queue could be useful; after
  80. a WILL negotiation was refused, a queue of WONT WILL would mean
  81. to request the option again. This seems of too little utility
  82. and too much potential waste; there is little chance that the
  83. other side will change its mind immediately.
  84. ** Here we don't have to generate another request because we've
  85. been "refused into" the correct state anyway.
  86. =end
  87. def handle_wont
  88. case @us
  89. when :no
  90. return nil, nil
  91. when :yes
  92. @him = :no
  93. return TELNET_DONT, @telopt
  94. when :wantno
  95. @him = :no
  96. return nil, nil
  97. when :wantno_opposite
  98. @him = :wantyes
  99. return TELNET_DO, @telopt
  100. when :wantyes
  101. @him = :no
  102. return nil, nil
  103. when :wantyes_opposite
  104. @him = :no
  105. return nil, nil
  106. end
  107. end
  108. =begin
  109. If we decide to ask him to enable:
  110. NO him=WANTYES, send DO.
  111. YES Error: Already enabled.
  112. WANTNO EMPTY If we are queueing requests, himq=OPPOSITE;
  113. otherwise, Error: Cannot initiate new request
  114. in the middle of negotiation.
  115. OPPOSITE Error: Already queued an enable request.
  116. WANTYES EMPTY Error: Already negotiating for enable.
  117. OPPOSITE himq=EMPTY.
  118. We handle the option on our side by the same procedures, with DO-
  119. WILL, DONT-WONT, him-us, himq-usq swapped.
  120. =end
  121. def handle_do
  122. case @him
  123. when :no
  124. @us = :wantyes
  125. return TELNET_WILL, @telopt
  126. when :yes
  127. return :error, 'Already enabled'
  128. when :wantno
  129. # us = :wantno_opposite # only if "buffering", whatever that means.
  130. return :error, 'Request in the middle of negotiation'
  131. when :wantno_opposite
  132. return :error, 'Already queued request'
  133. when :wantyes
  134. return :error, 'Already negotiating for enable'
  135. when :wantyes_opposite
  136. @us = :wantyes
  137. return nil, nil
  138. end
  139. end
  140. =begin
  141. If we decide to ask him to disable:
  142. NO Error: Already disabled.
  143. YES him=WANTNO, send DONT.
  144. WANTNO EMPTY Error: Already negotiating for disable.
  145. OPPOSITE himq=EMPTY.
  146. WANTYES EMPTY If we are queueing requests, himq=OPPOSITE;
  147. otherwise, Error: Cannot initiate new request
  148. in the middle of negotiation.
  149. OPPOSITE Error: Already queued a disable request.
  150. We handle the option on our side by the same procedures, with DO-
  151. WILL, DONT-WONT, him-us, himq-usq swapped.
  152. =end
  153. def handle_dont
  154. case @him
  155. when :no
  156. return :error, 'Already disabled'
  157. when :yes
  158. @us = :wantno
  159. return TELNET_WONT, @telopt
  160. when :wantno
  161. return :error, 'Already negotiating for disable'
  162. when :wantno_opposite
  163. @us = :wantno
  164. return nil, nil
  165. when :wantyes
  166. # us = :wantno_opposite # only if "buffering", whatever that means.
  167. return :error, 'Request in the middle of negotiation'
  168. when :wantyes_opposite
  169. return :error, 'Already queued disable request'
  170. end
  171. end
  172. # advertise willingess to support an option
  173. def send_will
  174. case @us
  175. when :no
  176. @us = :wantyes
  177. return TELNET_WILL, @telopt
  178. when :wantno
  179. @us = :wantno_opposite
  180. when :wantyes_opposite
  181. @us = :wantyes
  182. else
  183. return nil, nil
  184. end
  185. end
  186. # force turn-off of locally enabled option
  187. def send_wont
  188. case @us
  189. when :yes
  190. @us = :wantno
  191. return TELNET_WONT, @telopt
  192. when :wantno_opposite
  193. @us = :wantno
  194. return nil, nil
  195. when :wantyes
  196. @us = :wantyes_opposite
  197. return nil, nil
  198. else
  199. return nil, nil
  200. end
  201. end
  202. # ask remote end to enable an option
  203. def send_do
  204. case @him
  205. when :no
  206. @him = :wantyes
  207. return TELNET_DO, @telopt
  208. when :wantno
  209. @him = :wantno_opposite
  210. return nil, nil
  211. when :wantyes_opposite
  212. @us = :wantyes
  213. return nil, nil
  214. else
  215. return nil, nil
  216. end
  217. end
  218. # demand remote end disable an option
  219. def send_dont
  220. case @him
  221. when :yes
  222. @him = :wantno
  223. return TELNET_DONT, @telopt
  224. when :wantno_opposite
  225. @him = :wantno
  226. return nil, nil
  227. when :wantyes
  228. @him = :wantyes_opposite
  229. else
  230. return nil, nil
  231. end
  232. end
  233. end